mirror of
https://github.com/postgres/postgres.git
synced 2025-10-16 17:07:43 +03:00
Postgres95 1.01 Distribution - Virgin Sources
This commit is contained in:
29
src/backend/optimizer/Makefile.inc
Normal file
29
src/backend/optimizer/Makefile.inc
Normal file
@@ -0,0 +1,29 @@
|
||||
#-------------------------------------------------------------------------
|
||||
#
|
||||
# Makefile.inc--
|
||||
# Makefile for the optimizer module
|
||||
#
|
||||
# Copyright (c) 1994, Regents of the University of California
|
||||
#
|
||||
#
|
||||
# IDENTIFICATION
|
||||
# $Header: /cvsroot/pgsql/src/backend/optimizer/Attic/Makefile.inc,v 1.1.1.1 1996/07/09 06:21:34 scrappy Exp $
|
||||
#
|
||||
#-------------------------------------------------------------------------
|
||||
|
||||
optdir=$(CURDIR)/optimizer
|
||||
VPATH:=$(VPATH):$(optdir):\
|
||||
$(optdir)/path:$(optdir)/prep:$(optdir)/util:$(optdir)/plan
|
||||
|
||||
SUBSRCS=
|
||||
include $(optdir)/path/Makefile.inc
|
||||
include $(optdir)/prep/Makefile.inc
|
||||
include $(optdir)/util/Makefile.inc
|
||||
include $(optdir)/plan/Makefile.inc
|
||||
SRCS_OPTIMIZER:= $(SUBSRCS)
|
||||
|
||||
HEADERS+= clauseinfo.h clauses.h cost.h internal.h joininfo.h keys.h \
|
||||
ordering.h pathnode.h paths.h plancat.h planmain.h \
|
||||
planner.h prep.h tlist.h var.h xfunc.h
|
||||
|
||||
|
24
src/backend/optimizer/clauseinfo.h
Normal file
24
src/backend/optimizer/clauseinfo.h
Normal file
@@ -0,0 +1,24 @@
|
||||
/*-------------------------------------------------------------------------
|
||||
*
|
||||
* clauseinfo.h--
|
||||
* prototypes for clauseinfo.c.
|
||||
*
|
||||
*
|
||||
* Copyright (c) 1994, Regents of the University of California
|
||||
*
|
||||
* $Id: clauseinfo.h,v 1.1.1.1 1996/07/09 06:21:34 scrappy Exp $
|
||||
*
|
||||
*-------------------------------------------------------------------------
|
||||
*/
|
||||
#ifndef CLAUSEINFO_H
|
||||
#define CLAUSEINFO_H
|
||||
|
||||
extern bool valid_or_clause(CInfo *clauseinfo);
|
||||
extern List *get_actual_clauses(List *clauseinfo_list);
|
||||
extern void get_relattvals(List *clauseinfo_list, List **attnos,
|
||||
List **values, List **flags);
|
||||
extern void get_joinvars(Oid relid, List *clauseinfo_list,
|
||||
List **attnos, List **values, List **flags);
|
||||
extern List *get_opnos(List *clauseinfo_list);
|
||||
|
||||
#endif /* CLAUSEINFO_H */
|
54
src/backend/optimizer/clauses.h
Normal file
54
src/backend/optimizer/clauses.h
Normal file
@@ -0,0 +1,54 @@
|
||||
/*-------------------------------------------------------------------------
|
||||
*
|
||||
* clauses.h--
|
||||
* prototypes for clauses.c.
|
||||
*
|
||||
*
|
||||
* Copyright (c) 1994, Regents of the University of California
|
||||
*
|
||||
* $Id: clauses.h,v 1.1.1.1 1996/07/09 06:21:34 scrappy Exp $
|
||||
*
|
||||
*-------------------------------------------------------------------------
|
||||
*/
|
||||
#ifndef CLAUSES_H
|
||||
#define CLAUSES_H
|
||||
|
||||
#include "nodes/pg_list.h"
|
||||
#include "nodes/primnodes.h"
|
||||
|
||||
extern Expr *make_clause(int type, Node *oper, List *args);
|
||||
extern bool is_opclause(Node *clause);
|
||||
extern Expr *make_opclause(Oper *op, Var *leftop, Var *rightop);
|
||||
extern Var *get_leftop(Expr *clause);
|
||||
extern Var *get_rightop(Expr *clause);
|
||||
|
||||
extern bool agg_clause(Node *clause);
|
||||
|
||||
extern bool is_funcclause(Node *clause);
|
||||
extern Expr *make_funcclause(Func *func, List *funcargs);
|
||||
|
||||
extern bool or_clause(Node *clause);
|
||||
extern Expr *make_orclause(List *orclauses);
|
||||
|
||||
extern bool not_clause(Node *clause);
|
||||
extern Expr *make_notclause(Expr *notclause);
|
||||
extern Expr *get_notclausearg(Expr *notclause);
|
||||
|
||||
extern bool and_clause(Node *clause);
|
||||
extern Expr *make_andclause(List *andclauses);
|
||||
|
||||
extern List *pull_constant_clauses(List *quals, List **constantQual);
|
||||
extern void clause_relids_vars(Node *clause, List **relids, List **vars);
|
||||
extern int NumRelids(Node *clause);
|
||||
extern bool contains_not(Node *clause);
|
||||
extern bool join_clause_p(Node *clause);
|
||||
extern bool qual_clause_p(Node *clause);
|
||||
extern void fix_opid(Node *clause);
|
||||
extern List *fix_opids(List *clauses);
|
||||
extern void get_relattval(Node *clause, int *relid,
|
||||
AttrNumber *attno, Datum *constval, int *flag);
|
||||
extern void get_rels_atts(Node *clause, int *relid1,
|
||||
AttrNumber *attno1, int *relid2, AttrNumber *attno2);
|
||||
extern void CommuteClause(Node *clause);
|
||||
|
||||
#endif /* CLAUSES_H */
|
59
src/backend/optimizer/cost.h
Normal file
59
src/backend/optimizer/cost.h
Normal file
@@ -0,0 +1,59 @@
|
||||
/*-------------------------------------------------------------------------
|
||||
*
|
||||
* cost.h--
|
||||
* prototypes for costsize.c and clausesel.c.
|
||||
*
|
||||
*
|
||||
* Copyright (c) 1994, Regents of the University of California
|
||||
*
|
||||
* $Id: cost.h,v 1.1.1.1 1996/07/09 06:21:34 scrappy Exp $
|
||||
*
|
||||
*-------------------------------------------------------------------------
|
||||
*/
|
||||
#ifndef COST_H
|
||||
#define COST_H
|
||||
|
||||
/*
|
||||
* prototypes for costsize.c--
|
||||
* routines to compute costs and sizes
|
||||
*/
|
||||
extern bool _enable_seqscan_;
|
||||
extern bool _enable_indexscan_;
|
||||
extern bool _enable_sort_;
|
||||
extern bool _enable_hash_;
|
||||
extern bool _enable_nestloop_;
|
||||
extern bool _enable_mergesort_;
|
||||
extern bool _enable_hashjoin_;
|
||||
|
||||
extern Cost cost_seqscan(int relid, int relpages, int reltuples);
|
||||
extern Cost cost_index(Oid indexid, int expected_indexpages, Cost selec,
|
||||
int relpages, int reltuples, int indexpages,
|
||||
int indextuples, bool is_injoin);
|
||||
extern Cost cost_sort(List *keys, int tuples, int width, bool noread);
|
||||
extern Cost cost_result(int tuples, int width);
|
||||
extern Cost cost_nestloop(Cost outercost, Cost innercost, int outertuples,
|
||||
int innertuples, int outerpages, bool is_indexjoin);
|
||||
extern Cost cost_mergesort(Cost outercost, Cost innercost,
|
||||
List *outersortkeys, List *innersortkeys,
|
||||
int outersize, int innersize, int outerwidth, int innerwidth);
|
||||
extern Cost cost_hashjoin(Cost outercost, Cost innercost, List *outerkeys,
|
||||
List *innerkeys, int outersize, int innersize,
|
||||
int outerwidth, int innerwidth);
|
||||
extern int compute_rel_size(Rel *rel);
|
||||
extern int compute_rel_width(Rel *rel);
|
||||
extern int compute_targetlist_width(List *targetlist);
|
||||
extern int compute_joinrel_size(JoinPath *joinpath);
|
||||
extern int page_size(int tuples, int width);
|
||||
|
||||
/*
|
||||
* prototypes for fuctions in clausesel.h--
|
||||
* routines to compute clause selectivities
|
||||
*/
|
||||
extern void set_clause_selectivities(List *clauseinfo_list, Cost new_selectivity);
|
||||
extern Cost product_selec(List *clauseinfo_list);
|
||||
extern void set_rest_relselec(Query *root, List *rel_list);
|
||||
extern void set_rest_selec(Query *root,List *clauseinfo_list);
|
||||
extern Cost compute_clause_selec(Query *root,
|
||||
Node *clause, List *or_selectivities);
|
||||
|
||||
#endif /* COST_H */
|
92
src/backend/optimizer/internal.h
Normal file
92
src/backend/optimizer/internal.h
Normal file
@@ -0,0 +1,92 @@
|
||||
/*-------------------------------------------------------------------------
|
||||
*
|
||||
* internal.h--
|
||||
* Definitions required throughout the query optimizer.
|
||||
*
|
||||
*
|
||||
* Copyright (c) 1994, Regents of the University of California
|
||||
*
|
||||
* $Id: internal.h,v 1.1.1.1 1996/07/09 06:21:34 scrappy Exp $
|
||||
*
|
||||
*-------------------------------------------------------------------------
|
||||
*/
|
||||
#ifndef INTERNAL_H
|
||||
#define INTERNAL_H
|
||||
|
||||
/*
|
||||
* ---------- SHARED MACROS
|
||||
*
|
||||
* Macros common to modules for creating, accessing, and modifying
|
||||
* query tree and query plan components.
|
||||
* Shared with the executor.
|
||||
*
|
||||
*/
|
||||
|
||||
#include "nodes/nodes.h"
|
||||
#include "nodes/primnodes.h"
|
||||
#include "nodes/plannodes.h"
|
||||
#include "parser/parsetree.h"
|
||||
#include "nodes/relation.h"
|
||||
#include "catalog/pg_index.h" /* for INDEX_MAX_KEYS */
|
||||
#include "utils/syscache.h" /* for SearchSysCacheGetAttribute, etc. */
|
||||
|
||||
/*
|
||||
* System-dependent tuning constants
|
||||
*
|
||||
*/
|
||||
#define _CPU_PAGE_WEIGHT_ 0.065 /* CPU-to-page cost weighting factor */
|
||||
#define _PAGE_SIZE_ 8192 /* BLCKSZ (from ../h/bufmgr.h) */
|
||||
#define _MAX_KEYS_ INDEX_MAX_KEYS /* maximum number of keys in an index */
|
||||
#define _TID_SIZE_ 6 /* sizeof(itemid) (from ../h/itemid.h) */
|
||||
|
||||
/*
|
||||
* Size estimates
|
||||
*
|
||||
*/
|
||||
|
||||
/* The cost of sequentially scanning a materialized temporary relation
|
||||
*/
|
||||
#define _TEMP_SCAN_COST_ 10
|
||||
|
||||
/* The number of pages and tuples in a materialized relation
|
||||
*/
|
||||
#define _TEMP_RELATION_PAGES_ 1
|
||||
#define _TEMP_RELATION_TUPLES_ 10
|
||||
|
||||
/* The length of a variable-length field in bytes
|
||||
*/
|
||||
#define _DEFAULT_ATTRIBUTE_WIDTH_ (2 * _TID_SIZE_)
|
||||
|
||||
/*
|
||||
* Flags and identifiers
|
||||
*
|
||||
*/
|
||||
|
||||
/* Identifier for (sort) temp relations */
|
||||
/* used to be -1 */
|
||||
#define _TEMP_RELATION_ID_ InvalidOid
|
||||
|
||||
/* Identifier for invalid relation OIDs and attribute numbers for use by
|
||||
* selectivity functions
|
||||
*/
|
||||
#define _SELEC_VALUE_UNKNOWN_ -1
|
||||
|
||||
/* Flag indicating that a clause constant is really a parameter (or other
|
||||
* non-constant?), a non-parameter, or a constant on the right side
|
||||
* of the clause.
|
||||
*/
|
||||
#define _SELEC_NOT_CONSTANT_ 0
|
||||
#define _SELEC_IS_CONSTANT_ 1
|
||||
#define _SELEC_CONSTANT_LEFT_ 0
|
||||
#define _SELEC_CONSTANT_RIGHT_ 2
|
||||
|
||||
#define TOLERANCE 0.000001
|
||||
|
||||
#define FLOAT_EQUAL(X,Y) ((X) - (Y) < TOLERANCE)
|
||||
#define FLOAT_IS_ZERO(X) (FLOAT_EQUAL(X,0.0))
|
||||
|
||||
extern int BushyPlanFlag;
|
||||
/* #define deactivate_joininfo(joininfo) joininfo->inactive=true*/
|
||||
/*#define joininfo_inactive(joininfo) joininfo->inactive */
|
||||
|
||||
#endif /* INTERNAL_H */
|
20
src/backend/optimizer/joininfo.h
Normal file
20
src/backend/optimizer/joininfo.h
Normal file
@@ -0,0 +1,20 @@
|
||||
/*-------------------------------------------------------------------------
|
||||
*
|
||||
* joininfo.h--
|
||||
* prototypes for joininfo.c.
|
||||
*
|
||||
*
|
||||
* Copyright (c) 1994, Regents of the University of California
|
||||
*
|
||||
* $Id: joininfo.h,v 1.1.1.1 1996/07/09 06:21:34 scrappy Exp $
|
||||
*
|
||||
*-------------------------------------------------------------------------
|
||||
*/
|
||||
#ifndef JOININFO_H
|
||||
#define JOININFO_H
|
||||
|
||||
extern JInfo *joininfo_member(List *join_relids, List *joininfo_list);
|
||||
extern JInfo *find_joininfo_node(Rel *this_rel, List *join_relids);
|
||||
extern Var *other_join_clause_var(Var *var, Expr *clause);
|
||||
|
||||
#endif /* JOININFO_H */
|
22
src/backend/optimizer/keys.h
Normal file
22
src/backend/optimizer/keys.h
Normal file
@@ -0,0 +1,22 @@
|
||||
/*-------------------------------------------------------------------------
|
||||
*
|
||||
* keys.h--
|
||||
* prototypes for keys.c.
|
||||
*
|
||||
*
|
||||
* Copyright (c) 1994, Regents of the University of California
|
||||
*
|
||||
* $Id: keys.h,v 1.1.1.1 1996/07/09 06:21:34 scrappy Exp $
|
||||
*
|
||||
*-------------------------------------------------------------------------
|
||||
*/
|
||||
#ifndef KEYS_H
|
||||
#define KEYS_H
|
||||
|
||||
extern bool match_indexkey_operand(int indexkey, Var *operand, Rel *rel);
|
||||
extern bool equal_indexkey_var(int index_key, Var *var);
|
||||
extern Var *extract_subkey(JoinKey *jk, int which_subkey);
|
||||
extern bool samekeys(List *keys1, List *keys2);
|
||||
extern List *collect_index_pathkeys(int *index_keys, List *tlist);
|
||||
|
||||
#endif /* KEYS_H */
|
24
src/backend/optimizer/ordering.h
Normal file
24
src/backend/optimizer/ordering.h
Normal file
@@ -0,0 +1,24 @@
|
||||
/*-------------------------------------------------------------------------
|
||||
*
|
||||
* ordering.h--
|
||||
* prototypes for ordering.c.
|
||||
*
|
||||
*
|
||||
* Copyright (c) 1994, Regents of the University of California
|
||||
*
|
||||
* $Id: ordering.h,v 1.1.1.1 1996/07/09 06:21:34 scrappy Exp $
|
||||
*
|
||||
*-------------------------------------------------------------------------
|
||||
*/
|
||||
#ifndef ORDERING_H
|
||||
#define ORDERING_H
|
||||
|
||||
extern bool equal_path_path_ordering(PathOrder *path_ordering1,
|
||||
PathOrder *path_ordering2);
|
||||
extern bool equal_path_merge_ordering(Oid *path_ordering,
|
||||
MergeOrder *merge_ordering);
|
||||
extern bool equal_merge_merge_ordering(MergeOrder *merge_ordering1,
|
||||
MergeOrder *merge_ordering2);
|
||||
extern bool equal_sortops_order(Oid *ordering1, Oid *ordering2);
|
||||
|
||||
#endif /* ORDERING_H */
|
21
src/backend/optimizer/path/Makefile.inc
Normal file
21
src/backend/optimizer/path/Makefile.inc
Normal file
@@ -0,0 +1,21 @@
|
||||
#-------------------------------------------------------------------------
|
||||
#
|
||||
# Makefile.inc--
|
||||
# Makefile for optimizer/path
|
||||
#
|
||||
# Copyright (c) 1994, Regents of the University of California
|
||||
#
|
||||
#
|
||||
# IDENTIFICATION
|
||||
# $Header: /cvsroot/pgsql/src/backend/optimizer/path/Attic/Makefile.inc,v 1.1.1.1 1996/07/09 06:21:35 scrappy Exp $
|
||||
#
|
||||
#-------------------------------------------------------------------------
|
||||
|
||||
SUBSRCS= allpaths.c clausesel.c costsize.c hashutils.c indxpath.c \
|
||||
joinpath.c joinrels.c joinutils.c mergeutils.c orindxpath.c \
|
||||
prune.c
|
||||
|
||||
# not ready yet: predmig.c xfunc.c
|
||||
|
||||
|
||||
|
351
src/backend/optimizer/path/allpaths.c
Normal file
351
src/backend/optimizer/path/allpaths.c
Normal file
@@ -0,0 +1,351 @@
|
||||
/*-------------------------------------------------------------------------
|
||||
*
|
||||
* allpaths.c--
|
||||
* Routines to find possible search paths for processing a query
|
||||
*
|
||||
* Copyright (c) 1994, Regents of the University of California
|
||||
*
|
||||
*
|
||||
* IDENTIFICATION
|
||||
* $Header: /cvsroot/pgsql/src/backend/optimizer/path/allpaths.c,v 1.1.1.1 1996/07/09 06:21:35 scrappy Exp $
|
||||
*
|
||||
*-------------------------------------------------------------------------
|
||||
*/
|
||||
#include <string.h>
|
||||
|
||||
#include "postgres.h"
|
||||
|
||||
#include "nodes/pg_list.h"
|
||||
#include "nodes/relation.h"
|
||||
#include "nodes/primnodes.h"
|
||||
|
||||
#include "optimizer/internal.h"
|
||||
|
||||
#include "optimizer/paths.h"
|
||||
#include "optimizer/pathnode.h"
|
||||
#include "optimizer/clauses.h"
|
||||
#include "optimizer/xfunc.h"
|
||||
#include "optimizer/cost.h"
|
||||
|
||||
#include "commands/creatinh.h"
|
||||
|
||||
static void find_rel_paths(Query *root, List *rels);
|
||||
static List *find_join_paths(Query *root, List *outer_rels, int levels_left);
|
||||
|
||||
/*
|
||||
* find-paths--
|
||||
* Finds all possible access paths for executing a query, returning the
|
||||
* top level list of relation entries.
|
||||
*
|
||||
* 'rels' is the list of single relation entries appearing in the query
|
||||
*/
|
||||
List *
|
||||
find_paths(Query *root, List *rels)
|
||||
{
|
||||
int levels_left;
|
||||
|
||||
/*
|
||||
* Set the number of join (not nesting) levels yet to be processed.
|
||||
*/
|
||||
levels_left = length(rels);
|
||||
|
||||
if (levels_left <= 0)
|
||||
return NIL;
|
||||
|
||||
/*
|
||||
* Find the base relation paths.
|
||||
*/
|
||||
find_rel_paths(root, rels);
|
||||
|
||||
if (levels_left <= 1) {
|
||||
/*
|
||||
* Unsorted single relation, no more processing is required.
|
||||
*/
|
||||
return (rels);
|
||||
}else {
|
||||
/*
|
||||
* this means that joins or sorts are required.
|
||||
* set selectivities of clauses that have not been set
|
||||
* by an index.
|
||||
*/
|
||||
set_rest_relselec(root, rels);
|
||||
|
||||
return(find_join_paths(root, rels, levels_left-1));
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* find-rel-paths--
|
||||
* Finds all paths available for scanning each relation entry in
|
||||
* 'rels'. Sequential scan and any available indices are considered
|
||||
* if possible(indices are not considered for lower nesting levels).
|
||||
* All unique paths are attached to the relation's 'pathlist' field.
|
||||
*
|
||||
* MODIFIES: rels
|
||||
*/
|
||||
static void
|
||||
find_rel_paths(Query *root, List *rels)
|
||||
{
|
||||
List *temp;
|
||||
Rel *rel;
|
||||
List *lastpath;
|
||||
|
||||
foreach(temp, rels) {
|
||||
List *sequential_scan_list;
|
||||
List *rel_index_scan_list;
|
||||
List *or_index_scan_list;
|
||||
|
||||
rel = (Rel *)lfirst(temp);
|
||||
sequential_scan_list = lcons(create_seqscan_path(rel),
|
||||
NIL);
|
||||
|
||||
rel_index_scan_list =
|
||||
find_index_paths(root,
|
||||
rel,
|
||||
find_relation_indices(root,rel),
|
||||
rel->clauseinfo,
|
||||
rel->joininfo);
|
||||
|
||||
or_index_scan_list =
|
||||
create_or_index_paths(root, rel, rel->clauseinfo);
|
||||
|
||||
rel->pathlist = add_pathlist(rel,
|
||||
sequential_scan_list,
|
||||
append(rel_index_scan_list,
|
||||
or_index_scan_list));
|
||||
|
||||
/* The unordered path is always the last in the list.
|
||||
* If it is not the cheapest path, prune it.
|
||||
*/
|
||||
lastpath = rel->pathlist;
|
||||
while(lnext(lastpath)!=NIL)
|
||||
lastpath=lnext(lastpath);
|
||||
prune_rel_path(rel, (Path*)lfirst(lastpath));
|
||||
/*
|
||||
* if there is a qualification of sequential scan the selec.
|
||||
* value is not set -- so set it explicitly -- Sunita
|
||||
*/
|
||||
set_rest_selec(root, rel->clauseinfo);
|
||||
rel->size = compute_rel_size(rel);
|
||||
rel->width = compute_rel_width(rel);
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
/*
|
||||
* find-join-paths--
|
||||
* Find all possible joinpaths for a query by successively finding ways
|
||||
* to join single relations into join relations.
|
||||
*
|
||||
* if BushyPlanFlag is set, bushy tree plans will be generated:
|
||||
* Find all possible joinpaths(bushy trees) for a query by systematically
|
||||
* finding ways to join relations(both original and derived) together.
|
||||
*
|
||||
* 'outer-rels' is the current list of relations for which join paths
|
||||
* are to be found, i.e., he current list of relations that
|
||||
* have already been derived.
|
||||
* 'levels-left' is the current join level being processed, where '1' is
|
||||
* the "last" level
|
||||
*
|
||||
* Returns the final level of join relations, i.e., the relation that is
|
||||
* the result of joining all the original relations togehter.
|
||||
*/
|
||||
static List *
|
||||
find_join_paths(Query *root, List *outer_rels, int levels_left)
|
||||
{
|
||||
List *x;
|
||||
List *new_rels;
|
||||
Rel *rel;
|
||||
|
||||
/*
|
||||
* Determine all possible pairs of relations to be joined at this level.
|
||||
* Determine paths for joining these relation pairs and modify 'new-rels'
|
||||
* accordingly, then eliminate redundant join relations.
|
||||
*/
|
||||
new_rels = find_join_rels(root, outer_rels);
|
||||
|
||||
find_all_join_paths(root, new_rels);
|
||||
|
||||
new_rels = prune_joinrels(new_rels);
|
||||
|
||||
#if 0
|
||||
/*
|
||||
** for each expensive predicate in each path in each distinct rel,
|
||||
** consider doing pullup -- JMH
|
||||
*/
|
||||
if (XfuncMode != XFUNC_NOPULL && XfuncMode != XFUNC_OFF)
|
||||
foreach(x, new_rels)
|
||||
xfunc_trypullup((Rel*)lfirst(x));
|
||||
#endif
|
||||
|
||||
prune_rel_paths(new_rels);
|
||||
|
||||
if(BushyPlanFlag) {
|
||||
/*
|
||||
* In case of bushy trees
|
||||
* if there is still a join between a join relation and another
|
||||
* relation, add a new joininfo that involves the join relation
|
||||
* to the joininfo list of the other relation
|
||||
*/
|
||||
add_new_joininfos(root, new_rels,outer_rels);
|
||||
}
|
||||
|
||||
foreach(x, new_rels) {
|
||||
rel = (Rel*)lfirst(x);
|
||||
rel->size = compute_rel_size(rel);
|
||||
rel->width = compute_rel_width(rel);
|
||||
|
||||
/*#define OPTIMIZER_DEBUG*/
|
||||
#ifdef OPTIMIZER_DEBUG
|
||||
printf("levels left: %d\n", levels_left);
|
||||
debug_print_rel(root, rel);
|
||||
#endif
|
||||
}
|
||||
|
||||
if(BushyPlanFlag) {
|
||||
/*
|
||||
* prune rels that have been completely incorporated into
|
||||
* new join rels
|
||||
*/
|
||||
outer_rels = prune_oldrels(outer_rels);
|
||||
/*
|
||||
* merge join rels if then contain the same list of base rels
|
||||
*/
|
||||
outer_rels = merge_joinrels(new_rels,outer_rels);
|
||||
root->join_relation_list_ = outer_rels;
|
||||
}
|
||||
else {
|
||||
root->join_relation_list_ = new_rels;
|
||||
}
|
||||
|
||||
if(levels_left == 1) {
|
||||
if(BushyPlanFlag)
|
||||
return(final_join_rels(outer_rels));
|
||||
else
|
||||
return(new_rels);
|
||||
} else {
|
||||
if(BushyPlanFlag)
|
||||
return(find_join_paths(root, outer_rels, levels_left - 1));
|
||||
else
|
||||
return(find_join_paths(root, new_rels, levels_left - 1));
|
||||
}
|
||||
}
|
||||
|
||||
/*****************************************************************************
|
||||
*
|
||||
*****************************************************************************/
|
||||
|
||||
static void
|
||||
print_joinclauses(Query *root, List *clauses)
|
||||
{
|
||||
List *l;
|
||||
extern void print_expr(Node *expr, List *rtable); /* in print.c */
|
||||
|
||||
foreach(l, clauses) {
|
||||
CInfo *c = lfirst(l);
|
||||
|
||||
print_expr((Node*)c->clause, root->rtable);
|
||||
if (lnext(l)) printf(" ");
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
print_path(Query *root, Path *path, int indent)
|
||||
{
|
||||
char *ptype = NULL;
|
||||
JoinPath *jp;
|
||||
bool join;
|
||||
int i;
|
||||
|
||||
for(i=0; i < indent; i++)
|
||||
printf("\t");
|
||||
|
||||
switch(nodeTag(path)) {
|
||||
case T_Path:
|
||||
ptype = "SeqScan"; join=false; break;
|
||||
case T_IndexPath:
|
||||
ptype = "IdxScan"; join=false; break;
|
||||
case T_JoinPath:
|
||||
ptype = "Nestloop"; join=true; break;
|
||||
case T_MergePath:
|
||||
ptype = "MergeJoin"; join=true; break;
|
||||
case T_HashPath:
|
||||
ptype = "HashJoin"; join=true; break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
if (join) {
|
||||
int size = path->parent->size;
|
||||
jp = (JoinPath*)path;
|
||||
printf("%s size=%d cost=%f\n", ptype, size, path->path_cost);
|
||||
switch(nodeTag(path)) {
|
||||
case T_MergePath:
|
||||
case T_HashPath:
|
||||
for(i=0; i < indent+1; i++)
|
||||
printf("\t");
|
||||
printf(" clauses=(");
|
||||
print_joinclauses(root,
|
||||
((JoinPath*)path)->pathclauseinfo);
|
||||
printf(")\n");
|
||||
|
||||
if (nodeTag(path)==T_MergePath) {
|
||||
MergePath *mp = (MergePath*)path;
|
||||
if (mp->outersortkeys || mp->innersortkeys) {
|
||||
for(i=0; i < indent+1; i++)
|
||||
printf("\t");
|
||||
printf(" sortouter=%d sortinner=%d\n",
|
||||
((mp->outersortkeys)?1:0),
|
||||
((mp->innersortkeys)?1:0));
|
||||
}
|
||||
}
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
print_path(root, jp->outerjoinpath, indent+1);
|
||||
print_path(root, jp->innerjoinpath, indent+1);
|
||||
} else {
|
||||
int size = path->parent->size;
|
||||
int relid = lfirsti(path->parent->relids);
|
||||
printf("%s(%d) size=%d cost=%f",
|
||||
ptype, relid, size, path->path_cost);
|
||||
|
||||
if (nodeTag(path)==T_IndexPath) {
|
||||
List *k, *l;
|
||||
|
||||
printf(" keys=");
|
||||
foreach (k, path->keys) {
|
||||
printf("(");
|
||||
foreach (l, lfirst(k)) {
|
||||
Var *var = lfirst(l);
|
||||
printf("%d.%d", var->varnoold, var->varoattno);
|
||||
if (lnext(l)) printf(", ");
|
||||
}
|
||||
printf(")");
|
||||
if (lnext(k)) printf(", ");
|
||||
}
|
||||
}
|
||||
printf("\n");
|
||||
}
|
||||
}
|
||||
|
||||
#ifdef OPTIMIZER_DEBUG
|
||||
static void
|
||||
debug_print_rel(Query *root, Rel *rel)
|
||||
{
|
||||
List *l;
|
||||
|
||||
printf("(");
|
||||
foreach(l, rel->relids) {
|
||||
printf("%d ", lfirsti(l));
|
||||
}
|
||||
printf("): size=%d width=%d\n", rel->size, rel->width);
|
||||
|
||||
printf("\tpath list:\n");
|
||||
foreach (l, rel->pathlist) {
|
||||
print_path(root, lfirst(l), 1);
|
||||
}
|
||||
printf("\tcheapest path:\n");
|
||||
print_path(root, rel->cheapestpath, 1);
|
||||
}
|
||||
#endif /* OPTIMIZER_DEBUG */
|
331
src/backend/optimizer/path/clausesel.c
Normal file
331
src/backend/optimizer/path/clausesel.c
Normal file
@@ -0,0 +1,331 @@
|
||||
/*-------------------------------------------------------------------------
|
||||
*
|
||||
* clausesel.c--
|
||||
* Routines to compute and set clause selectivities
|
||||
*
|
||||
* Copyright (c) 1994, Regents of the University of California
|
||||
*
|
||||
*
|
||||
* IDENTIFICATION
|
||||
* $Header: /cvsroot/pgsql/src/backend/optimizer/path/clausesel.c,v 1.1.1.1 1996/07/09 06:21:35 scrappy Exp $
|
||||
*
|
||||
*-------------------------------------------------------------------------
|
||||
*/
|
||||
#include "postgres.h"
|
||||
|
||||
#include "nodes/pg_list.h"
|
||||
#include "nodes/relation.h"
|
||||
#include "nodes/primnodes.h"
|
||||
|
||||
#include "optimizer/internal.h"
|
||||
#include "optimizer/clauses.h"
|
||||
#include "optimizer/clauseinfo.h"
|
||||
#include "optimizer/cost.h"
|
||||
#include "optimizer/plancat.h"
|
||||
|
||||
#include "parser/parsetree.h" /* for getrelid() */
|
||||
|
||||
#include "catalog/pg_proc.h"
|
||||
#include "catalog/pg_operator.h"
|
||||
|
||||
#include "utils/elog.h"
|
||||
#include "utils/lsyscache.h"
|
||||
|
||||
static Cost compute_selec(Query *root, List *clauses, List *or_selectivities);
|
||||
|
||||
/****************************************************************************
|
||||
* ROUTINES TO SET CLAUSE SELECTIVITIES
|
||||
****************************************************************************/
|
||||
|
||||
/*
|
||||
* set_clause_selectivities -
|
||||
* Sets the selectivity field for each of clause in 'clauseinfo-list'
|
||||
* to 'new-selectivity'. If the selectivity has already been set, reset
|
||||
* it only if the new one is better.
|
||||
*
|
||||
* Returns nothing of interest.
|
||||
*
|
||||
*/
|
||||
void
|
||||
set_clause_selectivities(List *clauseinfo_list, Cost new_selectivity)
|
||||
{
|
||||
List *temp;
|
||||
CInfo *clausenode;
|
||||
Cost cost_clause;
|
||||
|
||||
foreach (temp,clauseinfo_list) {
|
||||
clausenode = (CInfo*)lfirst(temp);
|
||||
cost_clause = clausenode->selectivity;
|
||||
if ( FLOAT_IS_ZERO(cost_clause) || new_selectivity < cost_clause) {
|
||||
clausenode->selectivity = new_selectivity;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* product_selec -
|
||||
* Multiplies the selectivities of each clause in 'clauseinfo-list'.
|
||||
*
|
||||
* Returns a flonum corresponding to the selectivity of 'clauseinfo-list'.
|
||||
*/
|
||||
Cost
|
||||
product_selec(List *clauseinfo_list)
|
||||
{
|
||||
Cost result = 1.0;
|
||||
if (clauseinfo_list!=NIL) {
|
||||
List *xclausenode = NIL;
|
||||
Cost temp;
|
||||
|
||||
foreach(xclausenode,clauseinfo_list) {
|
||||
temp = ((CInfo *)lfirst(xclausenode))->selectivity;
|
||||
result = result * temp;
|
||||
}
|
||||
}
|
||||
return(result);
|
||||
}
|
||||
|
||||
/*
|
||||
* set_rest_relselec -
|
||||
* Scans through clauses on each relation and assigns a selectivity to
|
||||
* those clauses that haven't been assigned a selectivity by an index.
|
||||
*
|
||||
* Returns nothing of interest.
|
||||
* MODIFIES: selectivities of the various rel's clauseinfo
|
||||
* slots.
|
||||
*/
|
||||
void
|
||||
set_rest_relselec(Query *root, List *rel_list)
|
||||
{
|
||||
Rel *rel;
|
||||
List *x;
|
||||
|
||||
foreach (x,rel_list) {
|
||||
rel = (Rel*)lfirst(x);
|
||||
set_rest_selec(root, rel->clauseinfo);
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* set_rest_selec -
|
||||
* Sets the selectivity fields for those clauses within a single
|
||||
* relation's 'clauseinfo-list' that haven't already been set.
|
||||
*
|
||||
* Returns nothing of interest.
|
||||
*
|
||||
*/
|
||||
void
|
||||
set_rest_selec(Query *root, List *clauseinfo_list)
|
||||
{
|
||||
List *temp = NIL;
|
||||
CInfo *clausenode = (CInfo*)NULL;
|
||||
Cost cost_clause;
|
||||
|
||||
foreach (temp,clauseinfo_list) {
|
||||
clausenode = (CInfo*)lfirst(temp);
|
||||
cost_clause = clausenode->selectivity;
|
||||
|
||||
/*
|
||||
* Check to see if the selectivity of this clause or any 'or'
|
||||
* subclauses (if any) haven't been set yet.
|
||||
*/
|
||||
if (valid_or_clause(clausenode) || FLOAT_IS_ZERO(cost_clause)) {
|
||||
clausenode->selectivity =
|
||||
compute_clause_selec(root,
|
||||
(Node*)clausenode->clause,
|
||||
lcons(makeFloat(cost_clause), NIL));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/****************************************************************************
|
||||
* ROUTINES TO COMPUTE SELECTIVITIES
|
||||
****************************************************************************/
|
||||
|
||||
/*
|
||||
* compute_clause_selec -
|
||||
* Given a clause, this routine will compute the selectivity of the
|
||||
* clause by calling 'compute_selec' with the appropriate parameters
|
||||
* and possibly use that return value to compute the real selectivity
|
||||
* of a clause.
|
||||
*
|
||||
* 'or-selectivities' are selectivities that have already been assigned
|
||||
* to subclauses of an 'or' clause.
|
||||
*
|
||||
* Returns a flonum corresponding to the clause selectivity.
|
||||
*
|
||||
*/
|
||||
Cost
|
||||
compute_clause_selec(Query *root, Node *clause, List *or_selectivities)
|
||||
{
|
||||
if (!is_opclause (clause)) {
|
||||
/* if it's not an operator clause, then it is a boolean clause -jolly*/
|
||||
/*
|
||||
* Boolean variables get a selectivity of 1/2.
|
||||
*/
|
||||
return(0.1);
|
||||
} else if (not_clause (clause)) {
|
||||
/*
|
||||
* 'not' gets "1.0 - selectivity-of-inner-clause".
|
||||
*/
|
||||
return (1.000000 - compute_selec(root,
|
||||
lcons(get_notclausearg((Expr*)clause),
|
||||
NIL),
|
||||
or_selectivities));
|
||||
} else if (or_clause(clause)) {
|
||||
/*
|
||||
* Both 'or' and 'and' clauses are evaluated as described in
|
||||
* (compute_selec).
|
||||
*/
|
||||
return (compute_selec(root,
|
||||
((Expr*)clause)->args, or_selectivities));
|
||||
} else {
|
||||
return(compute_selec(root,
|
||||
lcons(clause,NIL),or_selectivities));
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* compute_selec -
|
||||
* Computes the selectivity of a clause.
|
||||
*
|
||||
* If there is more than one clause in the argument 'clauses', then the
|
||||
* desired selectivity is that of an 'or' clause. Selectivities for an
|
||||
* 'or' clause such as (OR a b) are computed by finding the selectivity
|
||||
* of a (s1) and b (s2) and computing s1+s2 - s1*s2.
|
||||
*
|
||||
* In addition, if the clause is an 'or' clause, individual selectivities
|
||||
* may have already been assigned by indices to subclauses. These values
|
||||
* are contained in the list 'or-selectivities'.
|
||||
*
|
||||
* Returns the clause selectivity as a flonum.
|
||||
*
|
||||
*/
|
||||
static Cost
|
||||
compute_selec(Query *root, List *clauses, List *or_selectivities)
|
||||
{
|
||||
Cost s1 = 0;
|
||||
List *clause = lfirst(clauses);
|
||||
|
||||
if (clauses==NULL) {
|
||||
s1 = 1.0;
|
||||
} else if (IsA(clause,Param)) {
|
||||
/* XXX How're we handling this before?? -ay */
|
||||
s1 = 1.0;
|
||||
} else if (IsA(clause,Const)) {
|
||||
s1 = ((bool) ((Const*) clause)->constvalue) ? 1.0 : 0.0;
|
||||
} else if (IsA(clause,Var)) {
|
||||
Oid relid = getrelid(((Var*)clause)->varno,
|
||||
root->rtable);
|
||||
|
||||
/*
|
||||
* we have a bool Var. This is exactly equivalent to the clause:
|
||||
* reln.attribute = 't'
|
||||
* so we compute the selectivity as if that is what we have. The
|
||||
* magic #define constants are a hack. I didn't want to have to
|
||||
* do system cache look ups to find out all of that info.
|
||||
*/
|
||||
|
||||
s1 = restriction_selectivity(EqualSelectivityProcedure,
|
||||
BooleanEqualOperator,
|
||||
relid,
|
||||
((Var*)clause)->varoattno,
|
||||
"t",
|
||||
_SELEC_CONSTANT_RIGHT_);
|
||||
} else if (or_selectivities) {
|
||||
/* If s1 has already been assigned by an index, use that value. */
|
||||
List *this_sel = lfirst(or_selectivities);
|
||||
|
||||
s1 = floatVal(this_sel);
|
||||
} else if (is_funcclause((Node*)clause)) {
|
||||
/* this isn't an Oper, it's a Func!! */
|
||||
/*
|
||||
** This is not an operator, so we guess at the selectivity.
|
||||
** THIS IS A HACK TO GET V4 OUT THE DOOR. FUNCS SHOULD BE
|
||||
** ABLE TO HAVE SELECTIVITIES THEMSELVES.
|
||||
** -- JMH 7/9/92
|
||||
*/
|
||||
s1 = 0.1;
|
||||
} else if (NumRelids((Node*) clause) == 1) {
|
||||
/* ...otherwise, calculate s1 from 'clauses'.
|
||||
* The clause is not a join clause, since there is
|
||||
* only one relid in the clause. The clause
|
||||
* selectivity will be based on the operator
|
||||
* selectivity and operand values.
|
||||
*/
|
||||
Oid opno = ((Oper*)((Expr*)clause)->oper)->opno;
|
||||
RegProcedure oprrest = get_oprrest(opno);
|
||||
Oid relid;
|
||||
int relidx;
|
||||
AttrNumber attno;
|
||||
Datum constval;
|
||||
int flag;
|
||||
|
||||
get_relattval((Node*)clause, &relidx, &attno, &constval, &flag);
|
||||
relid = getrelid(relidx, root->rtable);
|
||||
|
||||
/* if the oprrest procedure is missing for whatever reason,
|
||||
use a selectivity of 0.5*/
|
||||
if (!oprrest)
|
||||
s1 = (Cost) (0.5);
|
||||
else
|
||||
if (attno == InvalidAttrNumber) {
|
||||
/* attno can be Invalid if the clause had a function in it,
|
||||
i.e. WHERE myFunc(f) = 10 */
|
||||
/* this should be FIXED somehow to use function selectivity */
|
||||
s1 = (Cost) (0.5);
|
||||
} else
|
||||
s1 = (Cost) restriction_selectivity(oprrest,
|
||||
opno,
|
||||
relid,
|
||||
attno,
|
||||
(char *)constval,
|
||||
flag);
|
||||
|
||||
} else {
|
||||
/* The clause must be a join clause. The clause
|
||||
* selectivity will be based on the relations to be
|
||||
* scanned and the attributes they are to be joined
|
||||
* on.
|
||||
*/
|
||||
Oid opno = ((Oper*)((Expr*)clause)->oper)->opno;
|
||||
RegProcedure oprjoin = get_oprjoin (opno);
|
||||
int relid1, relid2;
|
||||
AttrNumber attno1, attno2;
|
||||
|
||||
get_rels_atts((Node*)clause, &relid1, &attno1, &relid2, &attno2);
|
||||
relid1 = getrelid(relid1, root->rtable);
|
||||
relid2 = getrelid(relid2, root->rtable);
|
||||
|
||||
/* if the oprjoin procedure is missing for whatever reason,
|
||||
use a selectivity of 0.5*/
|
||||
if (!oprjoin)
|
||||
s1 = (Cost) (0.5);
|
||||
else
|
||||
s1 = (Cost) join_selectivity(oprjoin,
|
||||
opno,
|
||||
relid1,
|
||||
attno1,
|
||||
relid2,
|
||||
attno2);
|
||||
}
|
||||
|
||||
/* A null clause list eliminates no tuples, so return a selectivity
|
||||
* of 1.0. If there is only one clause, the selectivity is not
|
||||
* that of an 'or' clause, but rather that of the single clause.
|
||||
*/
|
||||
|
||||
if (length (clauses) < 2) {
|
||||
return(s1);
|
||||
} else {
|
||||
/* Compute selectivity of the 'or'ed subclauses. */
|
||||
/* Added check for taking lnext(NIL). -- JMH 3/9/92 */
|
||||
Cost s2;
|
||||
|
||||
if (or_selectivities != NIL)
|
||||
s2 = compute_selec(root, lnext(clauses), lnext(or_selectivities));
|
||||
else
|
||||
s2 = compute_selec(root, lnext(clauses), NIL);
|
||||
return(s1 + s2 - s1 * s2);
|
||||
}
|
||||
}
|
||||
|
456
src/backend/optimizer/path/costsize.c
Normal file
456
src/backend/optimizer/path/costsize.c
Normal file
@@ -0,0 +1,456 @@
|
||||
/*-------------------------------------------------------------------------
|
||||
*
|
||||
* costsize.c--
|
||||
* Routines to compute (and set) relation sizes and path costs
|
||||
*
|
||||
* Copyright (c) 1994, Regents of the University of California
|
||||
*
|
||||
*
|
||||
* IDENTIFICATION
|
||||
* $Header: /cvsroot/pgsql/src/backend/optimizer/path/costsize.c,v 1.1.1.1 1996/07/09 06:21:35 scrappy Exp $
|
||||
*
|
||||
*-------------------------------------------------------------------------
|
||||
*/
|
||||
#include <math.h>
|
||||
#ifdef WIN32
|
||||
#include <float.h>
|
||||
#include <limits.h>
|
||||
#define MAXINT INT_MAX
|
||||
#else
|
||||
# if defined(PORTNAME_BSD44_derived) || defined(PORTNAME_bsdi)
|
||||
# include <machine/limits.h>
|
||||
# define MAXINT INT_MAX
|
||||
# else
|
||||
# include <values.h>
|
||||
# endif /* !PORTNAME_BSD44_derived */
|
||||
#endif /* WIN32 */
|
||||
|
||||
#include "postgres.h"
|
||||
|
||||
#include "nodes/relation.h"
|
||||
|
||||
#include "optimizer/cost.h"
|
||||
#include "optimizer/internal.h"
|
||||
#include "optimizer/keys.h"
|
||||
#include "optimizer/tlist.h"
|
||||
|
||||
#include "storage/bufmgr.h" /* for BLCKSZ */
|
||||
|
||||
static int compute_attribute_width(TargetEntry *tlistentry);
|
||||
static double base_log(double x, double b);
|
||||
|
||||
int _disable_cost_ = 30000000;
|
||||
|
||||
bool _enable_seqscan_ = true;
|
||||
bool _enable_indexscan_ = true;
|
||||
bool _enable_sort_ = true;
|
||||
bool _enable_hash_ = true;
|
||||
bool _enable_nestloop_ = true;
|
||||
bool _enable_mergesort_ = true;
|
||||
bool _enable_hashjoin_ = true;
|
||||
|
||||
/*
|
||||
* cost_seqscan--
|
||||
* Determines and returns the cost of scanning a relation sequentially.
|
||||
* If the relation is a temporary to be materialized from a query
|
||||
* embedded within a data field (determined by 'relid' containing an
|
||||
* attribute reference), then a predetermined constant is returned (we
|
||||
* have NO IDEA how big the result of a POSTQUEL procedure is going to
|
||||
* be).
|
||||
*
|
||||
* disk = p
|
||||
* cpu = *CPU-PAGE-WEIGHT* * t
|
||||
*
|
||||
* 'relid' is the relid of the relation to be scanned
|
||||
* 'relpages' is the number of pages in the relation to be scanned
|
||||
* (as determined from the system catalogs)
|
||||
* 'reltuples' is the number of tuples in the relation to be scanned
|
||||
*
|
||||
* Returns a flonum.
|
||||
*
|
||||
*/
|
||||
Cost
|
||||
cost_seqscan(int relid, int relpages, int reltuples)
|
||||
{
|
||||
Cost temp = 0;
|
||||
|
||||
if ( !_enable_seqscan_ )
|
||||
temp += _disable_cost_;
|
||||
|
||||
if (relid < 0) {
|
||||
/*
|
||||
* cost of sequentially scanning a materialized temporary relation
|
||||
*/
|
||||
temp += _TEMP_SCAN_COST_;
|
||||
} else {
|
||||
temp += relpages;
|
||||
temp += _CPU_PAGE_WEIGHT_ * reltuples;
|
||||
}
|
||||
Assert(temp >= 0);
|
||||
return(temp);
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* cost_index--
|
||||
* Determines and returns the cost of scanning a relation using an index.
|
||||
*
|
||||
* disk = expected-index-pages + expected-data-pages
|
||||
* cpu = *CPU-PAGE-WEIGHT* *
|
||||
* (expected-index-tuples + expected-data-tuples)
|
||||
*
|
||||
* 'indexid' is the index OID
|
||||
* 'expected-indexpages' is the number of index pages examined in the scan
|
||||
* 'selec' is the selectivity of the index
|
||||
* 'relpages' is the number of pages in the main relation
|
||||
* 'reltuples' is the number of tuples in the main relation
|
||||
* 'indexpages' is the number of pages in the index relation
|
||||
* 'indextuples' is the number of tuples in the index relation
|
||||
*
|
||||
* Returns a flonum.
|
||||
*
|
||||
*/
|
||||
Cost
|
||||
cost_index(Oid indexid,
|
||||
int expected_indexpages,
|
||||
Cost selec,
|
||||
int relpages,
|
||||
int reltuples,
|
||||
int indexpages,
|
||||
int indextuples,
|
||||
bool is_injoin)
|
||||
{
|
||||
Cost temp;
|
||||
Cost temp2;
|
||||
|
||||
temp = temp2 = (Cost) 0;
|
||||
|
||||
if (!_enable_indexscan_ && !is_injoin)
|
||||
temp += _disable_cost_;
|
||||
|
||||
/* expected index relation pages */
|
||||
temp += expected_indexpages;
|
||||
|
||||
/* about one base relation page */
|
||||
temp += Min(relpages,(int)ceil((double)selec*indextuples));
|
||||
|
||||
/*
|
||||
* per index tuple
|
||||
*/
|
||||
temp2 += selec * indextuples;
|
||||
temp2 += selec * reltuples;
|
||||
|
||||
temp = temp + (_CPU_PAGE_WEIGHT_ * temp2);
|
||||
Assert(temp >= 0);
|
||||
return(temp);
|
||||
}
|
||||
|
||||
/*
|
||||
* cost_sort--
|
||||
* Determines and returns the cost of sorting a relation by considering
|
||||
* 1. the cost of doing an external sort: XXX this is probably too low
|
||||
* disk = (p lg p)
|
||||
* cpu = *CPU-PAGE-WEIGHT* * (t lg t)
|
||||
* 2. the cost of reading the sort result into memory (another seqscan)
|
||||
* unless 'noread' is set
|
||||
*
|
||||
* 'keys' is a list of sort keys
|
||||
* 'tuples' is the number of tuples in the relation
|
||||
* 'width' is the average tuple width in bytes
|
||||
* 'noread' is a flag indicating that the sort result can remain on disk
|
||||
* (i.e., the sort result is the result relation)
|
||||
*
|
||||
* Returns a flonum.
|
||||
*
|
||||
*/
|
||||
Cost
|
||||
cost_sort(List *keys, int tuples, int width, bool noread)
|
||||
{
|
||||
Cost temp = 0;
|
||||
int npages = page_size (tuples,width);
|
||||
Cost pages = (Cost)npages;
|
||||
Cost numTuples = tuples;
|
||||
|
||||
if ( !_enable_sort_ )
|
||||
temp += _disable_cost_ ;
|
||||
if (tuples == 0 || keys==NULL)
|
||||
{
|
||||
Assert(temp >= 0);
|
||||
return(temp);
|
||||
}
|
||||
temp += pages * base_log((double)pages, (double)2.0);
|
||||
|
||||
/*
|
||||
* could be base_log(pages, NBuffers), but we are only doing 2-way merges
|
||||
*/
|
||||
temp += _CPU_PAGE_WEIGHT_ *
|
||||
numTuples * base_log((double)pages,(double)2.0);
|
||||
|
||||
if( !noread )
|
||||
temp = temp + cost_seqscan(_TEMP_RELATION_ID_, npages, tuples);
|
||||
Assert(temp >= 0);
|
||||
|
||||
return(temp);
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* cost_result--
|
||||
* Determines and returns the cost of writing a relation of 'tuples'
|
||||
* tuples of 'width' bytes out to a result relation.
|
||||
*
|
||||
* Returns a flonum.
|
||||
*
|
||||
*/
|
||||
Cost
|
||||
cost_result(int tuples, int width)
|
||||
{
|
||||
Cost temp =0;
|
||||
temp = temp + page_size(tuples,width);
|
||||
temp = temp + _CPU_PAGE_WEIGHT_ * tuples;
|
||||
Assert(temp >= 0);
|
||||
return(temp);
|
||||
}
|
||||
|
||||
/*
|
||||
* cost_nestloop--
|
||||
* Determines and returns the cost of joining two relations using the
|
||||
* nested loop algorithm.
|
||||
*
|
||||
* 'outercost' is the (disk+cpu) cost of scanning the outer relation
|
||||
* 'innercost' is the (disk+cpu) cost of scanning the inner relation
|
||||
* 'outertuples' is the number of tuples in the outer relation
|
||||
*
|
||||
* Returns a flonum.
|
||||
*
|
||||
*/
|
||||
Cost
|
||||
cost_nestloop(Cost outercost,
|
||||
Cost innercost,
|
||||
int outertuples,
|
||||
int innertuples,
|
||||
int outerpages,
|
||||
bool is_indexjoin)
|
||||
{
|
||||
Cost temp =0;
|
||||
|
||||
if ( !_enable_nestloop_ )
|
||||
temp += _disable_cost_;
|
||||
temp += outercost;
|
||||
temp += outertuples * innercost;
|
||||
Assert(temp >= 0);
|
||||
|
||||
return(temp);
|
||||
}
|
||||
|
||||
/*
|
||||
* cost_mergesort--
|
||||
* 'outercost' and 'innercost' are the (disk+cpu) costs of scanning the
|
||||
* outer and inner relations
|
||||
* 'outersortkeys' and 'innersortkeys' are lists of the keys to be used
|
||||
* to sort the outer and inner relations
|
||||
* 'outertuples' and 'innertuples' are the number of tuples in the outer
|
||||
* and inner relations
|
||||
* 'outerwidth' and 'innerwidth' are the (typical) widths (in bytes)
|
||||
* of the tuples of the outer and inner relations
|
||||
*
|
||||
* Returns a flonum.
|
||||
*
|
||||
*/
|
||||
Cost
|
||||
cost_mergesort(Cost outercost,
|
||||
Cost innercost,
|
||||
List *outersortkeys,
|
||||
List *innersortkeys,
|
||||
int outersize,
|
||||
int innersize,
|
||||
int outerwidth,
|
||||
int innerwidth)
|
||||
{
|
||||
Cost temp = 0;
|
||||
|
||||
if ( !_enable_mergesort_ )
|
||||
temp += _disable_cost_;
|
||||
|
||||
temp += outercost;
|
||||
temp += innercost;
|
||||
temp += cost_sort(outersortkeys,outersize,outerwidth,false);
|
||||
temp += cost_sort(innersortkeys,innersize,innerwidth,false);
|
||||
temp += _CPU_PAGE_WEIGHT_ * (outersize + innersize);
|
||||
Assert(temp >= 0);
|
||||
|
||||
return(temp);
|
||||
}
|
||||
|
||||
/*
|
||||
* cost_hashjoin-- XXX HASH
|
||||
* 'outercost' and 'innercost' are the (disk+cpu) costs of scanning the
|
||||
* outer and inner relations
|
||||
* 'outerkeys' and 'innerkeys' are lists of the keys to be used
|
||||
* to hash the outer and inner relations
|
||||
* 'outersize' and 'innersize' are the number of tuples in the outer
|
||||
* and inner relations
|
||||
* 'outerwidth' and 'innerwidth' are the (typical) widths (in bytes)
|
||||
* of the tuples of the outer and inner relations
|
||||
*
|
||||
* Returns a flonum.
|
||||
*/
|
||||
Cost
|
||||
cost_hashjoin(Cost outercost,
|
||||
Cost innercost,
|
||||
List *outerkeys,
|
||||
List *innerkeys,
|
||||
int outersize,
|
||||
int innersize,
|
||||
int outerwidth,
|
||||
int innerwidth)
|
||||
{
|
||||
Cost temp = 0;
|
||||
int outerpages = page_size (outersize,outerwidth);
|
||||
int innerpages = page_size (innersize,innerwidth);
|
||||
int nrun = ceil((double)outerpages/(double)NBuffers);
|
||||
|
||||
if (outerpages < innerpages)
|
||||
return _disable_cost_;
|
||||
if ( !_enable_hashjoin_ )
|
||||
temp += _disable_cost_;
|
||||
/* temp += outercost + (nrun + 1) * innercost; */
|
||||
/*
|
||||
the innercost shouldn't be used it. Instead the
|
||||
cost of hashing the innerpath should be used
|
||||
|
||||
ASSUME innercost is 1 for now -- a horrible hack
|
||||
- jolly
|
||||
*/
|
||||
temp += outercost + (nrun + 1);
|
||||
|
||||
temp += _CPU_PAGE_WEIGHT_ * (outersize + nrun * innersize);
|
||||
Assert(temp >= 0);
|
||||
|
||||
return(temp);
|
||||
}
|
||||
|
||||
/*
|
||||
* compute-rel-size--
|
||||
* Computes the size of each relation in 'rel-list' (after applying
|
||||
* restrictions), by multiplying the selectivity of each restriction
|
||||
* by the original size of the relation.
|
||||
*
|
||||
* Sets the 'size' field for each relation entry with this computed size.
|
||||
*
|
||||
* Returns the size.
|
||||
*/
|
||||
int compute_rel_size(Rel *rel)
|
||||
{
|
||||
Cost temp;
|
||||
int temp1;
|
||||
|
||||
temp = rel->tuples * product_selec(rel->clauseinfo);
|
||||
Assert(temp >= 0);
|
||||
if (temp >= (MAXINT - 1)) {
|
||||
temp1 = MAXINT;
|
||||
} else {
|
||||
temp1 = ceil((double) temp);
|
||||
}
|
||||
Assert(temp1 >= 0);
|
||||
Assert(temp1 <= MAXINT);
|
||||
return(temp1);
|
||||
}
|
||||
|
||||
/*
|
||||
* compute-rel-width--
|
||||
* Computes the width in bytes of a tuple from 'rel'.
|
||||
*
|
||||
* Returns the width of the tuple as a fixnum.
|
||||
*/
|
||||
int
|
||||
compute_rel_width(Rel *rel)
|
||||
{
|
||||
return (compute_targetlist_width(get_actual_tlist(rel->targetlist)));
|
||||
}
|
||||
|
||||
/*
|
||||
* compute-targetlist-width--
|
||||
* Computes the width in bytes of a tuple made from 'targetlist'.
|
||||
*
|
||||
* Returns the width of the tuple as a fixnum.
|
||||
*/
|
||||
int
|
||||
compute_targetlist_width(List *targetlist)
|
||||
{
|
||||
List *temp_tl;
|
||||
int tuple_width = 0;
|
||||
|
||||
foreach (temp_tl, targetlist) {
|
||||
tuple_width = tuple_width +
|
||||
compute_attribute_width(lfirst(temp_tl));
|
||||
}
|
||||
return(tuple_width);
|
||||
}
|
||||
|
||||
/*
|
||||
* compute-attribute-width--
|
||||
* Given a target list entry, find the size in bytes of the attribute.
|
||||
*
|
||||
* If a field is variable-length, it is assumed to be at least the size
|
||||
* of a TID field.
|
||||
*
|
||||
* Returns the width of the attribute as a fixnum.
|
||||
*/
|
||||
static int
|
||||
compute_attribute_width(TargetEntry *tlistentry)
|
||||
{
|
||||
int width = get_typlen(tlistentry->resdom->restype);
|
||||
if (width < 0)
|
||||
return(_DEFAULT_ATTRIBUTE_WIDTH_);
|
||||
else
|
||||
return(width);
|
||||
}
|
||||
|
||||
/*
|
||||
* compute-joinrel-size--
|
||||
* Computes the size of the join relation 'joinrel'.
|
||||
*
|
||||
* Returns a fixnum.
|
||||
*/
|
||||
int
|
||||
compute_joinrel_size(JoinPath *joinpath)
|
||||
{
|
||||
Cost temp = 1.0;
|
||||
int temp1 = 0;
|
||||
|
||||
temp *= ((Path*)joinpath->outerjoinpath)->parent->size;
|
||||
temp *= ((Path*)joinpath->innerjoinpath)->parent->size;
|
||||
|
||||
temp = temp * product_selec(joinpath->pathclauseinfo);
|
||||
if (temp >= (MAXINT -1)) {
|
||||
temp1 = MAXINT;
|
||||
} else {
|
||||
/* should be ceil here, we don't want joinrel size's of one, do we? */
|
||||
temp1 = ceil((double)temp);
|
||||
}
|
||||
Assert(temp1 >= 0);
|
||||
|
||||
return(temp1);
|
||||
}
|
||||
|
||||
/*
|
||||
* page-size--
|
||||
* Returns an estimate of the number of pages covered by a given
|
||||
* number of tuples of a given width (size in bytes).
|
||||
*/
|
||||
int page_size(int tuples, int width)
|
||||
{
|
||||
int temp =0;
|
||||
|
||||
temp = ceil((double)(tuples * (width + sizeof(HeapTupleData)))
|
||||
/ BLCKSZ);
|
||||
Assert(temp >= 0);
|
||||
return(temp);
|
||||
}
|
||||
|
||||
static double
|
||||
base_log(double x, double b)
|
||||
{
|
||||
return(log(x)/log(b));
|
||||
}
|
120
src/backend/optimizer/path/hashutils.c
Normal file
120
src/backend/optimizer/path/hashutils.c
Normal file
@@ -0,0 +1,120 @@
|
||||
/*-------------------------------------------------------------------------
|
||||
*
|
||||
* hashutils.c--
|
||||
* Utilities for finding applicable merge clauses and pathkeys
|
||||
*
|
||||
* Copyright (c) 1994, Regents of the University of California
|
||||
*
|
||||
*
|
||||
* IDENTIFICATION
|
||||
* $Header: /cvsroot/pgsql/src/backend/optimizer/path/Attic/hashutils.c,v 1.1.1.1 1996/07/09 06:21:35 scrappy Exp $
|
||||
*
|
||||
*-------------------------------------------------------------------------
|
||||
*/
|
||||
#include "postgres.h"
|
||||
#include "nodes/pg_list.h"
|
||||
#include "nodes/relation.h"
|
||||
|
||||
#include "optimizer/internal.h"
|
||||
#include "optimizer/paths.h"
|
||||
#include "optimizer/clauses.h"
|
||||
|
||||
|
||||
static HInfo *match_hashop_hashinfo(Oid hashop, List *hashinfo_list);
|
||||
|
||||
/*
|
||||
* group-clauses-by-hashop--
|
||||
* If a join clause node in 'clauseinfo-list' is hashjoinable, store
|
||||
* it within a hashinfo node containing other clause nodes with the same
|
||||
* hash operator.
|
||||
*
|
||||
* 'clauseinfo-list' is the list of clauseinfo nodes
|
||||
* 'inner-relid' is the relid of the inner join relation
|
||||
*
|
||||
* Returns the new list of hashinfo nodes.
|
||||
*
|
||||
*/
|
||||
List *
|
||||
group_clauses_by_hashop(List *clauseinfo_list,
|
||||
int inner_relid)
|
||||
{
|
||||
List *hashinfo_list = NIL;
|
||||
CInfo *clauseinfo = (CInfo*)NULL;
|
||||
List *i = NIL;
|
||||
Oid hashjoinop = 0;
|
||||
|
||||
foreach (i,clauseinfo_list) {
|
||||
clauseinfo = (CInfo*)lfirst(i);
|
||||
hashjoinop = clauseinfo->hashjoinoperator;
|
||||
|
||||
/*
|
||||
* Create a new hashinfo node and add it to 'hashinfo-list' if one
|
||||
* does not yet exist for this hash operator.
|
||||
*/
|
||||
if (hashjoinop ) {
|
||||
HInfo *xhashinfo = (HInfo*)NULL;
|
||||
Expr *clause = clauseinfo->clause;
|
||||
Var *leftop = get_leftop(clause);
|
||||
Var *rightop = get_rightop(clause);
|
||||
JoinKey *keys = (JoinKey*)NULL;
|
||||
|
||||
xhashinfo =
|
||||
match_hashop_hashinfo(hashjoinop,hashinfo_list);
|
||||
|
||||
if (inner_relid == leftop->varno){
|
||||
keys = makeNode(JoinKey);
|
||||
keys->outer = rightop;
|
||||
keys->inner = leftop;
|
||||
} else {
|
||||
keys = makeNode(JoinKey);
|
||||
keys->outer = leftop;
|
||||
keys->inner = rightop;
|
||||
}
|
||||
|
||||
if (xhashinfo==NULL) {
|
||||
xhashinfo = makeNode(HInfo);
|
||||
xhashinfo->hashop = hashjoinop;
|
||||
|
||||
xhashinfo->jmethod.jmkeys = NIL;
|
||||
xhashinfo->jmethod.clauses = NIL;
|
||||
|
||||
/* XXX was push */
|
||||
hashinfo_list = lappend(hashinfo_list,xhashinfo);
|
||||
hashinfo_list = nreverse(hashinfo_list);
|
||||
}
|
||||
|
||||
xhashinfo->jmethod.clauses =
|
||||
lcons(clause, xhashinfo->jmethod.clauses);
|
||||
|
||||
xhashinfo->jmethod.jmkeys =
|
||||
lcons(keys, xhashinfo->jmethod.jmkeys);
|
||||
}
|
||||
}
|
||||
return(hashinfo_list);
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* match-hashop-hashinfo--
|
||||
* Searches the list 'hashinfo-list' for a hashinfo node whose hash op
|
||||
* field equals 'hashop'.
|
||||
*
|
||||
* Returns the node if it exists.
|
||||
*
|
||||
*/
|
||||
static HInfo *
|
||||
match_hashop_hashinfo(Oid hashop, List *hashinfo_list)
|
||||
{
|
||||
Oid key = 0;
|
||||
HInfo *xhashinfo = (HInfo*)NULL;
|
||||
List *i = NIL;
|
||||
|
||||
foreach( i, hashinfo_list) {
|
||||
xhashinfo = (HInfo*)lfirst(i);
|
||||
key = xhashinfo->hashop;
|
||||
if (hashop == key) { /* found */
|
||||
return(xhashinfo); /* should be a hashinfo node ! */
|
||||
}
|
||||
}
|
||||
return((HInfo*)NIL);
|
||||
}
|
1206
src/backend/optimizer/path/indxpath.c
Normal file
1206
src/backend/optimizer/path/indxpath.c
Normal file
File diff suppressed because it is too large
Load Diff
623
src/backend/optimizer/path/joinpath.c
Normal file
623
src/backend/optimizer/path/joinpath.c
Normal file
@@ -0,0 +1,623 @@
|
||||
/*-------------------------------------------------------------------------
|
||||
*
|
||||
* joinpath.c--
|
||||
* Routines to find all possible paths for processing a set of joins
|
||||
*
|
||||
* Copyright (c) 1994, Regents of the University of California
|
||||
*
|
||||
*
|
||||
* IDENTIFICATION
|
||||
* $Header: /cvsroot/pgsql/src/backend/optimizer/path/joinpath.c,v 1.1.1.1 1996/07/09 06:21:35 scrappy Exp $
|
||||
*
|
||||
*-------------------------------------------------------------------------
|
||||
*/
|
||||
#include <math.h>
|
||||
|
||||
#include "storage/buf_internals.h"
|
||||
|
||||
#include "nodes/pg_list.h"
|
||||
#include "nodes/relation.h"
|
||||
#include "nodes/plannodes.h"
|
||||
|
||||
#include "optimizer/internal.h"
|
||||
#include "optimizer/paths.h"
|
||||
#include "optimizer/pathnode.h"
|
||||
#include "optimizer/keys.h"
|
||||
#include "optimizer/cost.h" /* for _enable_{hashjoin, _enable_mergesort} */
|
||||
|
||||
static Path *best_innerjoin(List *join_paths, List *outer_relid);
|
||||
static List *sort_inner_and_outer(Rel *joinrel, Rel *outerrel, Rel *innerrel,
|
||||
List *mergeinfo_list);
|
||||
static List *match_unsorted_outer(Rel *joinrel, Rel *outerrel, Rel *innerrel,
|
||||
List *outerpath_list, Path *cheapest_inner, Path *best_innerjoin,
|
||||
List *mergeinfo_list);
|
||||
static List *match_unsorted_inner(Rel *joinrel, Rel *outerrel, Rel *innerrel,
|
||||
List *innerpath_list, List *mergeinfo_list);
|
||||
static bool EnoughMemoryForHashjoin(Rel *hashrel);
|
||||
static List *hash_inner_and_outer(Rel *joinrel, Rel *outerrel, Rel *innerrel,
|
||||
List *hashinfo_list);
|
||||
|
||||
/*
|
||||
* find-all-join-paths--
|
||||
* Creates all possible ways to process joins for each of the join
|
||||
* relations in the list 'joinrels.' Each unique path will be included
|
||||
* in the join relation's 'pathlist' field.
|
||||
*
|
||||
* In postgres, n-way joins are handled left-only(permuting clauseless
|
||||
* joins doesn't usually win much).
|
||||
*
|
||||
* if BushyPlanFlag is true, bushy tree plans will be generated
|
||||
*
|
||||
* 'joinrels' is the list of relation entries to be joined
|
||||
*
|
||||
* Modifies the pathlist field of the appropriate rel node to contain
|
||||
* the unique join paths.
|
||||
* If bushy trees are considered, may modify the relid field of the
|
||||
* join rel nodes to flatten the lists.
|
||||
*
|
||||
* Returns nothing of interest. (?)
|
||||
* It does a destructive modification.
|
||||
*/
|
||||
void
|
||||
find_all_join_paths(Query *root, List *joinrels)
|
||||
{
|
||||
List *mergeinfo_list = NIL;
|
||||
List *hashinfo_list = NIL;
|
||||
List *temp_list = NIL;
|
||||
List *path = NIL;
|
||||
|
||||
while (joinrels != NIL) {
|
||||
Rel *joinrel = (Rel *)lfirst(joinrels);
|
||||
List *innerrelids;
|
||||
List *outerrelids;
|
||||
Rel *innerrel;
|
||||
Rel *outerrel;
|
||||
Path *bestinnerjoin;
|
||||
List *pathlist = NIL;
|
||||
|
||||
innerrelids = lsecond(joinrel->relids);
|
||||
outerrelids = lfirst(joinrel->relids);
|
||||
|
||||
/*
|
||||
* base relation id is an integer and join relation relid is a
|
||||
* list of integers.
|
||||
*/
|
||||
innerrel = (length(innerrelids)==1)?
|
||||
get_base_rel(root, lfirsti(innerrelids)) : get_join_rel(root,innerrelids);
|
||||
outerrel = (length(outerrelids)==1)?
|
||||
get_base_rel(root, lfirsti(outerrelids)) : get_join_rel(root, outerrelids);
|
||||
|
||||
bestinnerjoin = best_innerjoin(innerrel->innerjoin,
|
||||
outerrel->relids);
|
||||
if( _enable_mergesort_ ) {
|
||||
mergeinfo_list =
|
||||
group_clauses_by_order(joinrel->clauseinfo,
|
||||
lfirsti(innerrel->relids));
|
||||
}
|
||||
|
||||
if( _enable_hashjoin_ ) {
|
||||
hashinfo_list =
|
||||
group_clauses_by_hashop(joinrel->clauseinfo,
|
||||
lfirsti(innerrel->relids));
|
||||
}
|
||||
|
||||
/* need to flatten the relids list */
|
||||
joinrel->relids = intAppend(outerrelids, innerrelids);
|
||||
|
||||
/*
|
||||
* 1. Consider mergesort paths where both relations must be
|
||||
* explicitly sorted.
|
||||
*/
|
||||
pathlist = sort_inner_and_outer(joinrel,outerrel,
|
||||
innerrel,mergeinfo_list);
|
||||
|
||||
/*
|
||||
* 2. Consider paths where the outer relation need not be explicitly
|
||||
* sorted. This may include either nestloops and mergesorts where
|
||||
* the outer path is already ordered.
|
||||
*/
|
||||
pathlist =
|
||||
add_pathlist(joinrel, pathlist,
|
||||
match_unsorted_outer(joinrel,
|
||||
outerrel,
|
||||
innerrel,
|
||||
outerrel->pathlist,
|
||||
(Path*)innerrel->cheapestpath,
|
||||
bestinnerjoin,
|
||||
mergeinfo_list));
|
||||
|
||||
/*
|
||||
* 3. Consider paths where the inner relation need not be explicitly
|
||||
* sorted. This may include nestloops and mergesorts the actual
|
||||
* nestloop nodes were constructed in (match-unsorted-outer).
|
||||
*/
|
||||
pathlist =
|
||||
add_pathlist(joinrel,pathlist,
|
||||
match_unsorted_inner(joinrel,outerrel,
|
||||
innerrel,
|
||||
innerrel->pathlist,
|
||||
mergeinfo_list));
|
||||
|
||||
/*
|
||||
* 4. Consider paths where both outer and inner relations must be
|
||||
* hashed before being joined.
|
||||
*/
|
||||
|
||||
pathlist =
|
||||
add_pathlist(joinrel, pathlist,
|
||||
hash_inner_and_outer(joinrel,outerrel,
|
||||
innerrel,hashinfo_list));
|
||||
|
||||
joinrel->pathlist = pathlist;
|
||||
|
||||
/*
|
||||
* 'OuterJoinCost is only valid when calling (match-unsorted-inner)
|
||||
* with the same arguments as the previous invokation of
|
||||
* (match-unsorted-outer), so clear the field before going on.
|
||||
*/
|
||||
temp_list = innerrel->pathlist;
|
||||
foreach(path, temp_list) {
|
||||
|
||||
/*
|
||||
* XXX
|
||||
*
|
||||
* This gross hack is to get around an apparent optimizer bug on
|
||||
* Sparc (or maybe it is a bug of ours?) that causes really wierd
|
||||
* behavior.
|
||||
*/
|
||||
if (IsA_JoinPath(path)) {
|
||||
((Path*)lfirst(path))->outerjoincost = (Cost) 0;
|
||||
}
|
||||
|
||||
/* do it iff it is a join path, which is not always
|
||||
true, esp since the base level */
|
||||
}
|
||||
|
||||
joinrels = lnext(joinrels);
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* best-innerjoin--
|
||||
* Find the cheapest index path that has already been identified by
|
||||
* (indexable_joinclauses) as being a possible inner path for the given
|
||||
* outer relation in a nestloop join.
|
||||
*
|
||||
* 'join-paths' is a list of join nodes
|
||||
* 'outer-relid' is the relid of the outer join relation
|
||||
*
|
||||
* Returns the pathnode of the selected path.
|
||||
*/
|
||||
static Path *
|
||||
best_innerjoin(List *join_paths, List *outer_relids)
|
||||
{
|
||||
Path *cheapest = (Path*)NULL;
|
||||
List *join_path;
|
||||
|
||||
foreach(join_path, join_paths) {
|
||||
Path *path = (Path *)lfirst(join_path);
|
||||
|
||||
if (intMember(lfirsti(path->joinid), outer_relids)
|
||||
&& ((cheapest==NULL ||
|
||||
path_is_cheaper((Path*)lfirst(join_path),cheapest)))) {
|
||||
|
||||
cheapest = (Path*)lfirst(join_path);
|
||||
}
|
||||
}
|
||||
return(cheapest);
|
||||
}
|
||||
|
||||
/*
|
||||
* sort-inner-and-outer--
|
||||
* Create mergesort join paths by explicitly sorting both the outer and
|
||||
* inner join relations on each available merge ordering.
|
||||
*
|
||||
* 'joinrel' is the join relation
|
||||
* 'outerrel' is the outer join relation
|
||||
* 'innerrel' is the inner join relation
|
||||
* 'mergeinfo-list' is a list of nodes containing info on(mergesortable)
|
||||
* clauses for joining the relations
|
||||
*
|
||||
* Returns a list of mergesort paths.
|
||||
*/
|
||||
static List *
|
||||
sort_inner_and_outer(Rel *joinrel,
|
||||
Rel *outerrel,
|
||||
Rel *innerrel,
|
||||
List *mergeinfo_list)
|
||||
{
|
||||
List *ms_list = NIL;
|
||||
MInfo *xmergeinfo = (MInfo*)NULL;
|
||||
MergePath *temp_node = (MergePath*)NULL;
|
||||
List *i;
|
||||
List *outerkeys = NIL;
|
||||
List *innerkeys = NIL;
|
||||
List *merge_pathkeys = NIL;
|
||||
|
||||
foreach(i, mergeinfo_list) {
|
||||
xmergeinfo = (MInfo *)lfirst(i);
|
||||
|
||||
outerkeys =
|
||||
extract_path_keys(xmergeinfo->jmethod.jmkeys,
|
||||
outerrel->targetlist,
|
||||
OUTER);
|
||||
|
||||
innerkeys =
|
||||
extract_path_keys(xmergeinfo->jmethod.jmkeys,
|
||||
innerrel->targetlist,
|
||||
INNER);
|
||||
|
||||
merge_pathkeys =
|
||||
new_join_pathkeys(outerkeys, joinrel->targetlist,
|
||||
xmergeinfo->jmethod.clauses);
|
||||
|
||||
temp_node =
|
||||
create_mergesort_path(joinrel,
|
||||
outerrel->size,
|
||||
innerrel->size,
|
||||
outerrel->width,
|
||||
innerrel->width,
|
||||
(Path*)outerrel->cheapestpath,
|
||||
(Path*)innerrel->cheapestpath,
|
||||
merge_pathkeys,
|
||||
xmergeinfo->m_ordering,
|
||||
xmergeinfo->jmethod.clauses,
|
||||
outerkeys,
|
||||
innerkeys);
|
||||
|
||||
ms_list = lappend(ms_list, temp_node);
|
||||
}
|
||||
return(ms_list);
|
||||
}
|
||||
|
||||
/*
|
||||
* match-unsorted-outer--
|
||||
* Creates possible join paths for processing a single join relation
|
||||
* 'joinrel' by employing either iterative substitution or
|
||||
* mergesorting on each of its possible outer paths(assuming that the
|
||||
* outer relation need not be explicitly sorted).
|
||||
*
|
||||
* 1. The inner path is the cheapest available inner path.
|
||||
* 2. Mergesort wherever possible. Mergesorts are considered if there
|
||||
* are mergesortable join clauses between the outer and inner join
|
||||
* relations such that the outer path is keyed on the variables
|
||||
* appearing in the clauses. The corresponding inner merge path is
|
||||
* either a path whose keys match those of the outer path(if such a
|
||||
* path is available) or an explicit sort on the appropriate inner
|
||||
* join keys, whichever is cheaper.
|
||||
*
|
||||
* 'joinrel' is the join relation
|
||||
* 'outerrel' is the outer join relation
|
||||
* 'innerrel' is the inner join relation
|
||||
* 'outerpath-list' is the list of possible outer paths
|
||||
* 'cheapest-inner' is the cheapest inner path
|
||||
* 'best-innerjoin' is the best inner index path(if any)
|
||||
* 'mergeinfo-list' is a list of nodes containing info on mergesortable
|
||||
* clauses
|
||||
*
|
||||
* Returns a list of possible join path nodes.
|
||||
*/
|
||||
static List *
|
||||
match_unsorted_outer(Rel *joinrel,
|
||||
Rel *outerrel,
|
||||
Rel *innerrel,
|
||||
List *outerpath_list,
|
||||
Path *cheapest_inner,
|
||||
Path *best_innerjoin,
|
||||
List *mergeinfo_list)
|
||||
{
|
||||
Path *outerpath = (Path*)NULL;
|
||||
List *jp_list = NIL;
|
||||
List *temp_node = NIL;
|
||||
List *merge_pathkeys = NIL;
|
||||
Path *nestinnerpath =(Path*)NULL;
|
||||
List *paths = NIL;
|
||||
List *i = NIL;
|
||||
PathOrder *outerpath_ordering = NULL;
|
||||
|
||||
foreach(i,outerpath_list) {
|
||||
List *clauses = NIL;
|
||||
List *matchedJoinKeys = NIL;
|
||||
List *matchedJoinClauses = NIL;
|
||||
MInfo *xmergeinfo = (MInfo*)NULL;
|
||||
|
||||
outerpath = (Path*)lfirst(i);
|
||||
|
||||
outerpath_ordering = &outerpath->p_ordering;
|
||||
|
||||
if (outerpath_ordering) {
|
||||
xmergeinfo =
|
||||
match_order_mergeinfo(outerpath_ordering,
|
||||
mergeinfo_list);
|
||||
}
|
||||
|
||||
if (xmergeinfo) {
|
||||
clauses = xmergeinfo->jmethod.clauses;
|
||||
}
|
||||
|
||||
if (clauses) {
|
||||
List *keys = xmergeinfo->jmethod.jmkeys;
|
||||
List *clauses = xmergeinfo->jmethod.clauses;
|
||||
|
||||
matchedJoinKeys =
|
||||
match_pathkeys_joinkeys(outerpath->keys,
|
||||
keys,
|
||||
clauses,
|
||||
OUTER,
|
||||
&matchedJoinClauses);
|
||||
merge_pathkeys =
|
||||
new_join_pathkeys(outerpath->keys,
|
||||
joinrel->targetlist, clauses);
|
||||
} else {
|
||||
merge_pathkeys = outerpath->keys;
|
||||
}
|
||||
|
||||
if(best_innerjoin &&
|
||||
path_is_cheaper(best_innerjoin, cheapest_inner)) {
|
||||
nestinnerpath = best_innerjoin;
|
||||
} else {
|
||||
nestinnerpath = cheapest_inner;
|
||||
}
|
||||
|
||||
paths = lcons(create_nestloop_path(joinrel,
|
||||
outerrel,
|
||||
outerpath,
|
||||
nestinnerpath,
|
||||
merge_pathkeys),
|
||||
NIL);
|
||||
|
||||
if (clauses && matchedJoinKeys) {
|
||||
bool path_is_cheaper_than_sort;
|
||||
List *varkeys = NIL;
|
||||
Path *mergeinnerpath =
|
||||
match_paths_joinkeys(matchedJoinKeys,
|
||||
outerpath_ordering,
|
||||
innerrel->pathlist,
|
||||
INNER);
|
||||
|
||||
path_is_cheaper_than_sort =
|
||||
(bool) (mergeinnerpath &&
|
||||
(mergeinnerpath->path_cost <
|
||||
(cheapest_inner->path_cost +
|
||||
cost_sort(matchedJoinKeys,
|
||||
innerrel->size,
|
||||
innerrel->width,
|
||||
false))));
|
||||
if(!path_is_cheaper_than_sort) {
|
||||
varkeys =
|
||||
extract_path_keys(matchedJoinKeys,
|
||||
innerrel->targetlist,
|
||||
INNER);
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* Keep track of the cost of the outer path used with
|
||||
* this ordered inner path for later processing in
|
||||
* (match-unsorted-inner), since it isn't a sort and
|
||||
* thus wouldn't otherwise be considered.
|
||||
*/
|
||||
if (path_is_cheaper_than_sort) {
|
||||
mergeinnerpath->outerjoincost = outerpath->path_cost;
|
||||
} else {
|
||||
mergeinnerpath = cheapest_inner;
|
||||
}
|
||||
|
||||
temp_node =
|
||||
lcons(create_mergesort_path(joinrel,
|
||||
outerrel->size,
|
||||
innerrel->size,
|
||||
outerrel->width,
|
||||
innerrel->width,
|
||||
outerpath,
|
||||
mergeinnerpath,
|
||||
merge_pathkeys,
|
||||
xmergeinfo->m_ordering,
|
||||
matchedJoinClauses,
|
||||
NIL,
|
||||
varkeys),
|
||||
paths);
|
||||
} else {
|
||||
temp_node = paths;
|
||||
}
|
||||
jp_list = nconc(jp_list, temp_node);
|
||||
}
|
||||
return(jp_list);
|
||||
}
|
||||
|
||||
/*
|
||||
* match-unsorted-inner --
|
||||
* Find the cheapest ordered join path for a given(ordered, unsorted)
|
||||
* inner join path.
|
||||
*
|
||||
* Scans through each path available on an inner join relation and tries
|
||||
* matching its ordering keys against those of mergejoin clauses.
|
||||
* If 1. an appropriately-ordered inner path and matching mergeclause are
|
||||
* found, and
|
||||
* 2. sorting the cheapest outer path is cheaper than using an ordered
|
||||
* but unsorted outer path(as was considered in
|
||||
* (match-unsorted-outer)),
|
||||
* then this merge path is considered.
|
||||
*
|
||||
* 'joinrel' is the join result relation
|
||||
* 'outerrel' is the outer join relation
|
||||
* 'innerrel' is the inner join relation
|
||||
* 'innerpath-list' is the list of possible inner join paths
|
||||
* 'mergeinfo-list' is a list of nodes containing info on mergesortable
|
||||
* clauses
|
||||
*
|
||||
* Returns a list of possible merge paths.
|
||||
*/
|
||||
static List *
|
||||
match_unsorted_inner(Rel *joinrel,
|
||||
Rel *outerrel,
|
||||
Rel *innerrel,
|
||||
List *innerpath_list,
|
||||
List *mergeinfo_list)
|
||||
{
|
||||
Path *innerpath = (Path*)NULL;
|
||||
List *mp_list = NIL;
|
||||
List *temp_node = NIL;
|
||||
PathOrder *innerpath_ordering = NULL;
|
||||
Cost temp1 = 0.0;
|
||||
bool temp2 = false;
|
||||
List *i = NIL;
|
||||
|
||||
foreach (i, innerpath_list) {
|
||||
MInfo *xmergeinfo = (MInfo*)NULL;
|
||||
List *clauses = NIL;
|
||||
List *matchedJoinKeys = NIL;
|
||||
List *matchedJoinClauses = NIL;
|
||||
|
||||
innerpath = (Path*)lfirst(i);
|
||||
|
||||
innerpath_ordering = &innerpath->p_ordering;
|
||||
|
||||
if (innerpath_ordering) {
|
||||
xmergeinfo =
|
||||
match_order_mergeinfo(innerpath_ordering,
|
||||
mergeinfo_list);
|
||||
}
|
||||
|
||||
if (xmergeinfo) {
|
||||
clauses = ((JoinMethod*)xmergeinfo)->clauses;
|
||||
}
|
||||
|
||||
if (clauses) {
|
||||
List *keys = xmergeinfo->jmethod.jmkeys;
|
||||
List *cls = xmergeinfo->jmethod.clauses;
|
||||
|
||||
matchedJoinKeys =
|
||||
match_pathkeys_joinkeys(innerpath->keys,
|
||||
keys,
|
||||
cls,
|
||||
INNER,
|
||||
&matchedJoinClauses);
|
||||
}
|
||||
|
||||
/*
|
||||
* (match-unsorted-outer) if it is applicable.
|
||||
* 'OuterJoinCost was set above in
|
||||
*/
|
||||
if (clauses && matchedJoinKeys) {
|
||||
temp1 = outerrel->cheapestpath->path_cost +
|
||||
cost_sort(matchedJoinKeys, outerrel->size, outerrel->width,
|
||||
false);
|
||||
|
||||
temp2 = (bool) (FLOAT_IS_ZERO(innerpath->outerjoincost)
|
||||
|| (innerpath->outerjoincost > temp1));
|
||||
|
||||
if(temp2) {
|
||||
List *outerkeys =
|
||||
extract_path_keys(matchedJoinKeys,
|
||||
outerrel->targetlist,
|
||||
OUTER);
|
||||
List *merge_pathkeys =
|
||||
new_join_pathkeys(outerkeys,
|
||||
joinrel->targetlist,
|
||||
clauses);
|
||||
|
||||
temp_node =
|
||||
lcons(create_mergesort_path(joinrel,
|
||||
outerrel->size,
|
||||
innerrel->size,
|
||||
outerrel->width,
|
||||
innerrel->width,
|
||||
(Path*)outerrel->cheapestpath,
|
||||
innerpath,
|
||||
merge_pathkeys,
|
||||
xmergeinfo->m_ordering,
|
||||
matchedJoinClauses,
|
||||
outerkeys,
|
||||
NIL),
|
||||
NIL);
|
||||
|
||||
mp_list = nconc(mp_list,temp_node);
|
||||
}
|
||||
}
|
||||
}
|
||||
return(mp_list);
|
||||
|
||||
}
|
||||
|
||||
static bool
|
||||
EnoughMemoryForHashjoin(Rel *hashrel)
|
||||
{
|
||||
int ntuples;
|
||||
int tupsize;
|
||||
int pages;
|
||||
|
||||
ntuples = hashrel->size;
|
||||
if (ntuples == 0) ntuples = 1000;
|
||||
tupsize = hashrel->width + sizeof(HeapTupleData);
|
||||
pages = page_size(ntuples, tupsize);
|
||||
/*
|
||||
* if amount of buffer space below hashjoin threshold,
|
||||
* return false
|
||||
*/
|
||||
if (ceil(sqrt((double)pages)) > NBuffers)
|
||||
return false;
|
||||
return true;
|
||||
}
|
||||
|
||||
/*
|
||||
* hash-inner-and-outer-- XXX HASH
|
||||
* Create hashjoin join paths by explicitly hashing both the outer and
|
||||
* inner join relations on each available hash op.
|
||||
*
|
||||
* 'joinrel' is the join relation
|
||||
* 'outerrel' is the outer join relation
|
||||
* 'innerrel' is the inner join relation
|
||||
* 'hashinfo-list' is a list of nodes containing info on(hashjoinable)
|
||||
* clauses for joining the relations
|
||||
*
|
||||
* Returns a list of hashjoin paths.
|
||||
*/
|
||||
static List *
|
||||
hash_inner_and_outer(Rel *joinrel,
|
||||
Rel *outerrel,
|
||||
Rel *innerrel,
|
||||
List *hashinfo_list)
|
||||
{
|
||||
HInfo *xhashinfo = (HInfo*)NULL;
|
||||
List *hjoin_list = NIL;
|
||||
HashPath *temp_node = (HashPath*)NULL;
|
||||
List *i = NIL;
|
||||
List *outerkeys = NIL;
|
||||
List *innerkeys = NIL;
|
||||
List *hash_pathkeys = NIL;
|
||||
|
||||
foreach (i, hashinfo_list) {
|
||||
xhashinfo = (HInfo*)lfirst(i);
|
||||
outerkeys =
|
||||
extract_path_keys(((JoinMethod*)xhashinfo)->jmkeys,
|
||||
outerrel->targetlist,
|
||||
OUTER);
|
||||
innerkeys =
|
||||
extract_path_keys(((JoinMethod*)xhashinfo)->jmkeys,
|
||||
innerrel->targetlist,
|
||||
INNER);
|
||||
hash_pathkeys =
|
||||
new_join_pathkeys(outerkeys,
|
||||
joinrel->targetlist,
|
||||
((JoinMethod*)xhashinfo)->clauses);
|
||||
|
||||
if (EnoughMemoryForHashjoin(innerrel)) {
|
||||
temp_node = create_hashjoin_path(joinrel,
|
||||
outerrel->size,
|
||||
innerrel->size,
|
||||
outerrel->width,
|
||||
innerrel->width,
|
||||
(Path*)outerrel->cheapestpath,
|
||||
(Path*)innerrel->cheapestpath,
|
||||
hash_pathkeys,
|
||||
xhashinfo->hashop,
|
||||
((JoinMethod*)xhashinfo)->clauses,
|
||||
outerkeys,
|
||||
innerkeys);
|
||||
hjoin_list = lappend(hjoin_list, temp_node);
|
||||
}
|
||||
}
|
||||
return(hjoin_list);
|
||||
}
|
||||
|
528
src/backend/optimizer/path/joinrels.c
Normal file
528
src/backend/optimizer/path/joinrels.c
Normal file
@@ -0,0 +1,528 @@
|
||||
/*-------------------------------------------------------------------------
|
||||
*
|
||||
* joinrels.c--
|
||||
* Routines to determine which relations should be joined
|
||||
*
|
||||
* Copyright (c) 1994, Regents of the University of California
|
||||
*
|
||||
*
|
||||
* IDENTIFICATION
|
||||
* $Header: /cvsroot/pgsql/src/backend/optimizer/path/joinrels.c,v 1.1.1.1 1996/07/09 06:21:36 scrappy Exp $
|
||||
*
|
||||
*-------------------------------------------------------------------------
|
||||
*/
|
||||
#include "postgres.h"
|
||||
|
||||
#include "nodes/pg_list.h"
|
||||
#include "nodes/relation.h"
|
||||
|
||||
#include "optimizer/internal.h"
|
||||
#include "optimizer/cost.h"
|
||||
#include "optimizer/paths.h"
|
||||
#include "optimizer/tlist.h"
|
||||
#include "optimizer/joininfo.h"
|
||||
#include "optimizer/pathnode.h"
|
||||
|
||||
|
||||
static List *find_clause_joins(Query *root, Rel *outer_rel, List *joininfo_list);
|
||||
static List *find_clauseless_joins(Rel *outer_rel, List *inner_rels);
|
||||
static Rel *init_join_rel(Rel *outer_rel, Rel *inner_rel, JInfo *joininfo);
|
||||
static List *new_join_tlist(List *tlist, List *other_relids,
|
||||
int first_resdomno);
|
||||
static List *new_joininfo_list(List *joininfo_list, List *join_relids);
|
||||
static void add_superrels(Rel *rel, Rel *super_rel);
|
||||
static bool nonoverlap_rels(Rel *rel1, Rel *rel2);
|
||||
static bool nonoverlap_sets(List *s1, List *s2);
|
||||
static void set_joinrel_size(Rel *joinrel, Rel *outer_rel, Rel *inner_rel,
|
||||
JInfo *jinfo);
|
||||
|
||||
/*
|
||||
* find-join-rels--
|
||||
* Find all possible joins for each of the outer join relations in
|
||||
* 'outer-rels'. A rel node is created for each possible join relation,
|
||||
* and the resulting list of nodes is returned. If at all possible, only
|
||||
* those relations for which join clauses exist are considered. If none
|
||||
* of these exist for a given relation, all remaining possibilities are
|
||||
* considered.
|
||||
*
|
||||
* 'outer-rels' is the list of rel nodes
|
||||
*
|
||||
* Returns a list of rel nodes corresponding to the new join relations.
|
||||
*/
|
||||
List *
|
||||
find_join_rels(Query *root, List *outer_rels)
|
||||
{
|
||||
List *joins = NIL;
|
||||
List *join_list = NIL;
|
||||
List *r = NIL;
|
||||
|
||||
foreach(r, outer_rels) {
|
||||
Rel *outer_rel = (Rel *)lfirst(r);
|
||||
|
||||
if(!(joins = find_clause_joins(root, outer_rel,outer_rel->joininfo)))
|
||||
if (BushyPlanFlag)
|
||||
joins = find_clauseless_joins(outer_rel,outer_rels);
|
||||
else
|
||||
joins = find_clauseless_joins(outer_rel,root->base_relation_list_);
|
||||
|
||||
join_list = nconc(join_list, joins);
|
||||
}
|
||||
|
||||
return(join_list);
|
||||
}
|
||||
|
||||
/*
|
||||
* find-clause-joins--
|
||||
* Determines whether joins can be performed between an outer relation
|
||||
* 'outer-rel' and those relations within 'outer-rel's joininfo nodes
|
||||
* (i.e., relations that participate in join clauses that 'outer-rel'
|
||||
* participates in). This is possible if all but one of the relations
|
||||
* contained within the join clauses of the joininfo node are already
|
||||
* contained within 'outer-rel'.
|
||||
*
|
||||
* 'outer-rel' is the relation entry for the outer relation
|
||||
* 'joininfo-list' is a list of join clauses which 'outer-rel'
|
||||
* participates in
|
||||
*
|
||||
* Returns a list of new join relations.
|
||||
*/
|
||||
static List *
|
||||
find_clause_joins(Query *root, Rel *outer_rel, List *joininfo_list)
|
||||
{
|
||||
List *join_list = NIL;
|
||||
List *i = NIL;
|
||||
|
||||
foreach (i, joininfo_list) {
|
||||
JInfo *joininfo = (JInfo*)lfirst(i);
|
||||
Rel *rel;
|
||||
|
||||
if(!joininfo->inactive) {
|
||||
List *other_rels = joininfo->otherrels;
|
||||
|
||||
if(other_rels != NIL) {
|
||||
if(length(other_rels) == 1) {
|
||||
rel = init_join_rel(outer_rel,
|
||||
get_base_rel(root, lfirsti(other_rels)),
|
||||
joininfo);
|
||||
} else if (BushyPlanFlag) {
|
||||
rel = init_join_rel(outer_rel,
|
||||
get_join_rel(root, other_rels),
|
||||
joininfo);
|
||||
} else {
|
||||
rel = NULL;
|
||||
}
|
||||
|
||||
if (rel != NULL)
|
||||
join_list = lappend(join_list, rel);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return(join_list);
|
||||
}
|
||||
|
||||
/*
|
||||
* find-clauseless-joins--
|
||||
* Given an outer relation 'outer-rel' and a list of inner relations
|
||||
* 'inner-rels', create a join relation between 'outer-rel' and each
|
||||
* member of 'inner-rels' that isn't already included in 'outer-rel'.
|
||||
*
|
||||
* Returns a list of new join relations.
|
||||
*/
|
||||
static List *
|
||||
find_clauseless_joins(Rel *outer_rel, List *inner_rels)
|
||||
{
|
||||
Rel *inner_rel;
|
||||
List *t_list = NIL;
|
||||
List *temp_node = NIL;
|
||||
List *i = NIL;
|
||||
|
||||
foreach (i, inner_rels) {
|
||||
inner_rel = (Rel *)lfirst(i);
|
||||
if(nonoverlap_rels(inner_rel, outer_rel)) {
|
||||
temp_node = lcons(init_join_rel(outer_rel,
|
||||
inner_rel,
|
||||
(JInfo*)NULL),
|
||||
NIL);
|
||||
t_list = nconc(t_list,temp_node);
|
||||
}
|
||||
}
|
||||
|
||||
return(t_list);
|
||||
}
|
||||
|
||||
/*
|
||||
* init-join-rel--
|
||||
* Creates and initializes a new join relation.
|
||||
*
|
||||
* 'outer-rel' and 'inner-rel' are relation nodes for the relations to be
|
||||
* joined
|
||||
* 'joininfo' is the joininfo node(join clause) containing both
|
||||
* 'outer-rel' and 'inner-rel', if any exists
|
||||
*
|
||||
* Returns the new join relation node.
|
||||
*/
|
||||
static Rel *
|
||||
init_join_rel(Rel *outer_rel, Rel *inner_rel, JInfo *joininfo)
|
||||
{
|
||||
Rel *joinrel = makeNode(Rel);
|
||||
List *joinrel_joininfo_list = NIL;
|
||||
List *new_outer_tlist;
|
||||
List *new_inner_tlist;
|
||||
|
||||
/*
|
||||
* Create a new tlist by removing irrelevant elements from both
|
||||
* tlists of the outer and inner join relations and then merging
|
||||
* the results together.
|
||||
*/
|
||||
new_outer_tlist =
|
||||
new_join_tlist(outer_rel->targetlist, /* XXX 1-based attnos */
|
||||
inner_rel->relids, 1);
|
||||
new_inner_tlist =
|
||||
new_join_tlist(inner_rel->targetlist, /* XXX 1-based attnos */
|
||||
outer_rel->relids,
|
||||
length(new_outer_tlist) + 1);
|
||||
|
||||
joinrel->relids = NIL;
|
||||
joinrel->indexed = false;
|
||||
joinrel->pages = 0;
|
||||
joinrel->tuples = 0;
|
||||
joinrel->width = 0;
|
||||
/* joinrel->targetlist = NIL;*/
|
||||
joinrel->pathlist = NIL;
|
||||
joinrel->unorderedpath = (Path *)NULL;
|
||||
joinrel->cheapestpath = (Path *)NULL;
|
||||
joinrel->pruneable = true;
|
||||
joinrel->classlist = NULL;
|
||||
joinrel->relam = InvalidOid;
|
||||
joinrel->ordering = NULL;
|
||||
joinrel->clauseinfo = NIL;
|
||||
joinrel->joininfo = NULL;
|
||||
joinrel->innerjoin = NIL;
|
||||
joinrel->superrels = NIL;
|
||||
|
||||
joinrel->relids = lcons(outer_rel->relids, /* ??? aren't they lists? -ay */
|
||||
lcons(inner_rel->relids, NIL));
|
||||
|
||||
new_outer_tlist = nconc(new_outer_tlist,new_inner_tlist);
|
||||
joinrel->targetlist = new_outer_tlist;
|
||||
|
||||
if (joininfo) {
|
||||
joinrel->clauseinfo = joininfo->jinfoclauseinfo;
|
||||
if (BushyPlanFlag)
|
||||
joininfo->inactive = true;
|
||||
}
|
||||
|
||||
joinrel_joininfo_list =
|
||||
new_joininfo_list(append(outer_rel->joininfo, inner_rel->joininfo),
|
||||
intAppend(outer_rel->relids, inner_rel->relids));
|
||||
|
||||
joinrel->joininfo = joinrel_joininfo_list;
|
||||
|
||||
set_joinrel_size(joinrel, outer_rel, inner_rel, joininfo);
|
||||
|
||||
return(joinrel);
|
||||
}
|
||||
|
||||
/*
|
||||
* new-join-tlist--
|
||||
* Builds a join relations's target list by keeping those elements that
|
||||
* will be in the final target list and any other elements that are still
|
||||
* needed for future joins. For a target list entry to still be needed
|
||||
* for future joins, its 'joinlist' field must not be empty after removal
|
||||
* of all relids in 'other-relids'.
|
||||
*
|
||||
* 'tlist' is the target list of one of the join relations
|
||||
* 'other-relids' is a list of relids contained within the other
|
||||
* join relation
|
||||
* 'first-resdomno' is the resdom number to use for the first created
|
||||
* target list entry
|
||||
*
|
||||
* Returns the new target list.
|
||||
*/
|
||||
static List *
|
||||
new_join_tlist(List *tlist,
|
||||
List *other_relids,
|
||||
int first_resdomno)
|
||||
{
|
||||
int resdomno = first_resdomno - 1;
|
||||
TargetEntry *xtl = NULL;
|
||||
List *temp_node = NIL;
|
||||
List *t_list = NIL;
|
||||
List *i = NIL;
|
||||
List *join_list = NIL;
|
||||
bool in_final_tlist =false;
|
||||
|
||||
|
||||
foreach(i,tlist) {
|
||||
xtl= lfirst(i);
|
||||
in_final_tlist = (join_list==NIL);
|
||||
if( in_final_tlist) {
|
||||
resdomno += 1;
|
||||
temp_node =
|
||||
lcons(create_tl_element(get_expr(xtl),
|
||||
resdomno),
|
||||
NIL);
|
||||
t_list = nconc(t_list,temp_node);
|
||||
}
|
||||
}
|
||||
|
||||
return(t_list);
|
||||
}
|
||||
|
||||
/*
|
||||
* new-joininfo-list--
|
||||
* Builds a join relation's joininfo list by checking for join clauses
|
||||
* which still need to used in future joins involving this relation. A
|
||||
* join clause is still needed if there are still relations in the clause
|
||||
* not contained in the list of relations comprising this join relation.
|
||||
* New joininfo nodes are only created and added to
|
||||
* 'current-joininfo-list' if a node for a particular join hasn't already
|
||||
* been created.
|
||||
*
|
||||
* 'current-joininfo-list' contains a list of those joininfo nodes that
|
||||
* have already been built
|
||||
* 'joininfo-list' is the list of join clauses involving this relation
|
||||
* 'join-relids' is a list of relids corresponding to the relations
|
||||
* currently being joined
|
||||
*
|
||||
* Returns a list of joininfo nodes, new and old.
|
||||
*/
|
||||
static List *
|
||||
new_joininfo_list(List *joininfo_list, List *join_relids)
|
||||
{
|
||||
List *current_joininfo_list = NIL;
|
||||
List *new_otherrels = NIL;
|
||||
JInfo *other_joininfo = (JInfo*)NULL;
|
||||
List *xjoininfo = NIL;
|
||||
|
||||
foreach (xjoininfo, joininfo_list) {
|
||||
JInfo *joininfo = (JInfo*)lfirst(xjoininfo);
|
||||
|
||||
new_otherrels = joininfo->otherrels;
|
||||
if (nonoverlap_sets(new_otherrels,join_relids)) {
|
||||
other_joininfo = joininfo_member(new_otherrels,
|
||||
current_joininfo_list);
|
||||
if(other_joininfo) {
|
||||
other_joininfo->jinfoclauseinfo =
|
||||
(List*)LispUnion(joininfo->jinfoclauseinfo,
|
||||
other_joininfo->jinfoclauseinfo);
|
||||
}else {
|
||||
other_joininfo = makeNode(JInfo);
|
||||
|
||||
other_joininfo->otherrels =
|
||||
joininfo->otherrels;
|
||||
other_joininfo->jinfoclauseinfo =
|
||||
joininfo->jinfoclauseinfo;
|
||||
other_joininfo->mergesortable =
|
||||
joininfo->mergesortable;
|
||||
other_joininfo->hashjoinable =
|
||||
joininfo->hashjoinable;
|
||||
other_joininfo->inactive = false;
|
||||
|
||||
current_joininfo_list = lcons(other_joininfo,
|
||||
current_joininfo_list);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return(current_joininfo_list);
|
||||
}
|
||||
|
||||
/*
|
||||
* add-new-joininfos--
|
||||
* For each new join relation, create new joininfos that
|
||||
* use the join relation as inner relation, and add
|
||||
* the new joininfos to those rel nodes that still
|
||||
* have joins with the join relation.
|
||||
*
|
||||
* 'joinrels' is a list of join relations.
|
||||
*
|
||||
* Modifies the joininfo field of appropriate rel nodes.
|
||||
*/
|
||||
void
|
||||
add_new_joininfos(Query *root, List *joinrels, List *outerrels)
|
||||
{
|
||||
List *xjoinrel = NIL;
|
||||
List *xrelid = NIL;
|
||||
List *xrel = NIL;
|
||||
List *xjoininfo = NIL;
|
||||
|
||||
foreach(xjoinrel, joinrels) {
|
||||
Rel *joinrel = (Rel *)lfirst(xjoinrel);
|
||||
foreach(xrelid, joinrel->relids) {
|
||||
Relid relid = (Relid)lfirst(xrelid);
|
||||
Rel *rel = get_join_rel(root, relid);
|
||||
add_superrels(rel,joinrel);
|
||||
}
|
||||
}
|
||||
foreach(xjoinrel, joinrels) {
|
||||
Rel *joinrel = (Rel *)lfirst(xjoinrel);
|
||||
|
||||
foreach(xjoininfo, joinrel->joininfo) {
|
||||
JInfo *joininfo = (JInfo*)lfirst(xjoininfo);
|
||||
List *other_rels = joininfo->otherrels;
|
||||
List *clause_info = joininfo->jinfoclauseinfo;
|
||||
bool mergesortable = joininfo->mergesortable;
|
||||
bool hashjoinable = joininfo->hashjoinable;
|
||||
|
||||
foreach(xrelid, other_rels) {
|
||||
Relid relid = (Relid)lfirst(xrelid);
|
||||
Rel *rel = get_join_rel(root, relid);
|
||||
List *super_rels = rel->superrels;
|
||||
List *xsuper_rel = NIL;
|
||||
JInfo *new_joininfo = makeNode(JInfo);
|
||||
|
||||
new_joininfo->otherrels = joinrel->relids;
|
||||
new_joininfo->jinfoclauseinfo = clause_info;
|
||||
new_joininfo->mergesortable = mergesortable;
|
||||
new_joininfo->hashjoinable = hashjoinable;
|
||||
new_joininfo->inactive = false;
|
||||
rel->joininfo =
|
||||
lappend(rel->joininfo, new_joininfo);
|
||||
|
||||
foreach(xsuper_rel, super_rels) {
|
||||
Rel *super_rel = (Rel *)lfirst(xsuper_rel);
|
||||
|
||||
if( nonoverlap_rels(super_rel,joinrel) ) {
|
||||
List *new_relids = super_rel->relids;
|
||||
JInfo *other_joininfo =
|
||||
joininfo_member(new_relids,
|
||||
joinrel->joininfo);
|
||||
|
||||
if (other_joininfo) {
|
||||
other_joininfo->jinfoclauseinfo =
|
||||
(List*)LispUnion(clause_info,
|
||||
other_joininfo->jinfoclauseinfo);
|
||||
} else {
|
||||
JInfo *new_joininfo = makeNode(JInfo);
|
||||
|
||||
new_joininfo->otherrels = new_relids;
|
||||
new_joininfo->jinfoclauseinfo = clause_info;
|
||||
new_joininfo->mergesortable = mergesortable;
|
||||
new_joininfo->hashjoinable = hashjoinable;
|
||||
new_joininfo->inactive = false;
|
||||
joinrel->joininfo =
|
||||
lappend(joinrel->joininfo,
|
||||
new_joininfo);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
foreach(xrel, outerrels) {
|
||||
Rel *rel = (Rel *)lfirst(xrel);
|
||||
rel->superrels = NIL;
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* final-join-rels--
|
||||
* Find the join relation that includes all the original
|
||||
* relations, i.e. the final join result.
|
||||
*
|
||||
* 'join-rel-list' is a list of join relations.
|
||||
*
|
||||
* Returns the list of final join relations.
|
||||
*/
|
||||
List *
|
||||
final_join_rels(List *join_rel_list)
|
||||
{
|
||||
List *xrel = NIL;
|
||||
List *temp = NIL;
|
||||
List *t_list = NIL;
|
||||
|
||||
/*
|
||||
* find the relations that has no further joins,
|
||||
* i.e., its joininfos all have otherrels nil.
|
||||
*/
|
||||
foreach(xrel,join_rel_list) {
|
||||
Rel *rel = (Rel *)lfirst(xrel);
|
||||
List *xjoininfo = NIL;
|
||||
bool final = true;
|
||||
|
||||
foreach (xjoininfo, rel->joininfo) {
|
||||
JInfo *joininfo = (JInfo*)lfirst(xjoininfo);
|
||||
|
||||
if (joininfo->otherrels != NIL) {
|
||||
final = false;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (final) {
|
||||
temp = lcons(rel, NIL);
|
||||
t_list = nconc(t_list, temp);
|
||||
}
|
||||
}
|
||||
|
||||
return(t_list);
|
||||
}
|
||||
|
||||
/*
|
||||
* add_superrels--
|
||||
* add rel to the temporary property list superrels.
|
||||
*
|
||||
* 'rel' a rel node
|
||||
* 'super-rel' rel node of a join relation that includes rel
|
||||
*
|
||||
* Modifies the superrels field of rel
|
||||
*/
|
||||
static void
|
||||
add_superrels(Rel *rel, Rel *super_rel)
|
||||
{
|
||||
rel->superrels = lappend(rel->superrels, super_rel);
|
||||
}
|
||||
|
||||
/*
|
||||
* nonoverlap-rels--
|
||||
* test if two join relations overlap, i.e., includes the same
|
||||
* relation.
|
||||
*
|
||||
* 'rel1' and 'rel2' are two join relations
|
||||
*
|
||||
* Returns non-nil if rel1 and rel2 do not overlap.
|
||||
*/
|
||||
static bool
|
||||
nonoverlap_rels(Rel *rel1, Rel *rel2)
|
||||
{
|
||||
return(nonoverlap_sets(rel1->relids, rel2->relids));
|
||||
}
|
||||
|
||||
static bool
|
||||
nonoverlap_sets(List *s1, List *s2)
|
||||
{
|
||||
List *x = NIL;
|
||||
|
||||
foreach(x,s1) {
|
||||
int e = lfirsti(x);
|
||||
if(intMember(e,s2))
|
||||
return(false);
|
||||
}
|
||||
return(true);
|
||||
}
|
||||
|
||||
static void
|
||||
set_joinrel_size(Rel *joinrel, Rel *outer_rel, Rel *inner_rel, JInfo *jinfo)
|
||||
{
|
||||
int ntuples;
|
||||
float selec;
|
||||
|
||||
/* voodoo magic. but better than a size of 0. I have no idea why
|
||||
we didn't set the size before. -ay 2/95 */
|
||||
if (jinfo==NULL) {
|
||||
/* worst case: the cartesian product */
|
||||
ntuples = outer_rel->tuples * inner_rel->tuples;
|
||||
} else {
|
||||
selec = product_selec(jinfo->jinfoclauseinfo);
|
||||
/* ntuples = Min(outer_rel->tuples,inner_rel->tuples) * selec; */
|
||||
ntuples = outer_rel->tuples * inner_rel->tuples * selec;
|
||||
}
|
||||
|
||||
/* I bet sizes less than 1 will screw up optimization so
|
||||
make the best case 1 instead of 0 - jolly*/
|
||||
if (ntuples < 1)
|
||||
ntuples = 1;
|
||||
|
||||
joinrel->tuples = ntuples;
|
||||
}
|
432
src/backend/optimizer/path/joinutils.c
Normal file
432
src/backend/optimizer/path/joinutils.c
Normal file
@@ -0,0 +1,432 @@
|
||||
/*-------------------------------------------------------------------------
|
||||
*
|
||||
* joinutils.c--
|
||||
* Utilities for matching and building join and path keys
|
||||
*
|
||||
* Copyright (c) 1994, Regents of the University of California
|
||||
*
|
||||
*
|
||||
* IDENTIFICATION
|
||||
* $Header: /cvsroot/pgsql/src/backend/optimizer/path/Attic/joinutils.c,v 1.1.1.1 1996/07/09 06:21:36 scrappy Exp $
|
||||
*
|
||||
*-------------------------------------------------------------------------
|
||||
*/
|
||||
#include "postgres.h"
|
||||
|
||||
#include "nodes/pg_list.h"
|
||||
#include "nodes/relation.h"
|
||||
#include "nodes/plannodes.h"
|
||||
|
||||
#include "optimizer/internal.h"
|
||||
#include "optimizer/paths.h"
|
||||
#include "optimizer/var.h"
|
||||
#include "optimizer/keys.h"
|
||||
#include "optimizer/tlist.h"
|
||||
#include "optimizer/joininfo.h"
|
||||
#include "optimizer/ordering.h"
|
||||
|
||||
|
||||
static int match_pathkey_joinkeys(List *pathkey, List *joinkeys,
|
||||
int which_subkey);
|
||||
static bool every_func(List *joinkeys, List *pathkey,
|
||||
int which_subkey);
|
||||
static List *new_join_pathkey(List *subkeys,
|
||||
List *considered_subkeys, List *join_rel_tlist,
|
||||
List *joinclauses);
|
||||
static List *new_matching_subkeys(Var *subkey, List *considered_subkeys,
|
||||
List *join_rel_tlist, List *joinclauses);
|
||||
|
||||
/****************************************************************************
|
||||
* KEY COMPARISONS
|
||||
****************************************************************************/
|
||||
|
||||
/*
|
||||
* match-pathkeys-joinkeys--
|
||||
* Attempts to match the keys of a path against the keys of join clauses.
|
||||
* This is done by looking for a matching join key in 'joinkeys' for
|
||||
* every path key in the list 'pathkeys'. If there is a matching join key
|
||||
* (not necessarily unique) for every path key, then the list of
|
||||
* corresponding join keys and join clauses are returned in the order in
|
||||
* which the keys matched the path keys.
|
||||
*
|
||||
* 'pathkeys' is a list of path keys:
|
||||
* ( ( (var) (var) ... ) ( (var) ... ) )
|
||||
* 'joinkeys' is a list of join keys:
|
||||
* ( (outer inner) (outer inner) ... )
|
||||
* 'joinclauses' is a list of clauses corresponding to the join keys in
|
||||
* 'joinkeys'
|
||||
* 'which-subkey' is a flag that selects the desired subkey of a join key
|
||||
* in 'joinkeys'
|
||||
*
|
||||
* Returns the join keys and corresponding join clauses in a list if all
|
||||
* of the path keys were matched:
|
||||
* (
|
||||
* ( (outerkey0 innerkey0) ... (outerkeyN innerkeyN) )
|
||||
* ( clause0 ... clauseN )
|
||||
* )
|
||||
* and nil otherwise.
|
||||
*
|
||||
* Returns a list of matched join keys and a list of matched join clauses
|
||||
* in matchedJoinClausesPtr. - ay 11/94
|
||||
*/
|
||||
List *
|
||||
match_pathkeys_joinkeys(List *pathkeys,
|
||||
List *joinkeys,
|
||||
List *joinclauses,
|
||||
int which_subkey,
|
||||
List **matchedJoinClausesPtr)
|
||||
{
|
||||
List *matched_joinkeys = NIL;
|
||||
List *matched_joinclauses = NIL;
|
||||
List *pathkey = NIL;
|
||||
List *i = NIL;
|
||||
int matched_joinkey_index = -1;
|
||||
|
||||
foreach(i, pathkeys) {
|
||||
pathkey = lfirst(i);
|
||||
matched_joinkey_index =
|
||||
match_pathkey_joinkeys(pathkey, joinkeys, which_subkey);
|
||||
|
||||
if (matched_joinkey_index != -1 ) {
|
||||
List *xjoinkey = nth(matched_joinkey_index,joinkeys);
|
||||
List *joinclause = nth(matched_joinkey_index,joinclauses);
|
||||
|
||||
/* XXX was "push" function */
|
||||
matched_joinkeys = lappend(matched_joinkeys,xjoinkey);
|
||||
matched_joinkeys = nreverse(matched_joinkeys);
|
||||
|
||||
matched_joinclauses = lappend(matched_joinclauses,joinclause);
|
||||
matched_joinclauses = nreverse(matched_joinclauses);
|
||||
joinkeys = LispRemove(xjoinkey,joinkeys);
|
||||
} else {
|
||||
return(NIL);
|
||||
}
|
||||
|
||||
}
|
||||
if(matched_joinkeys==NULL ||
|
||||
length(matched_joinkeys) != length(pathkeys)) {
|
||||
return NIL;
|
||||
}
|
||||
|
||||
*matchedJoinClausesPtr = nreverse(matched_joinclauses);
|
||||
return (nreverse(matched_joinkeys));
|
||||
}
|
||||
|
||||
/*
|
||||
* match-pathkey-joinkeys--
|
||||
* Returns the 0-based index into 'joinkeys' of the first joinkey whose
|
||||
* outer or inner subkey matches any subkey of 'pathkey'.
|
||||
*/
|
||||
static int
|
||||
match_pathkey_joinkeys(List *pathkey,
|
||||
List *joinkeys,
|
||||
int which_subkey)
|
||||
{
|
||||
Var *path_subkey;
|
||||
int pos;
|
||||
List *i = NIL;
|
||||
List *x = NIL;
|
||||
JoinKey *jk;
|
||||
|
||||
foreach(i, pathkey) {
|
||||
path_subkey = (Var *)lfirst(i);
|
||||
pos = 0;
|
||||
foreach(x, joinkeys) {
|
||||
jk = (JoinKey*)lfirst(x);
|
||||
if(var_equal(path_subkey,
|
||||
extract_subkey(jk, which_subkey)))
|
||||
return(pos);
|
||||
pos++;
|
||||
}
|
||||
}
|
||||
return(-1); /* no index found */
|
||||
}
|
||||
|
||||
/*
|
||||
* match-paths-joinkeys--
|
||||
* Attempts to find a path in 'paths' whose keys match a set of join
|
||||
* keys 'joinkeys'. To match,
|
||||
* 1. the path node ordering must equal 'ordering'.
|
||||
* 2. each subkey of a given path must match(i.e., be(var_equal) to) the
|
||||
* appropriate subkey of the corresponding join key in 'joinkeys',
|
||||
* i.e., the Nth path key must match its subkeys against the subkey of
|
||||
* the Nth join key in 'joinkeys'.
|
||||
*
|
||||
* 'joinkeys' is the list of key pairs to which the path keys must be
|
||||
* matched
|
||||
* 'ordering' is the ordering of the(outer) path to which 'joinkeys'
|
||||
* must correspond
|
||||
* 'paths' is a list of(inner) paths which are to be matched against
|
||||
* each join key in 'joinkeys'
|
||||
* 'which-subkey' is a flag that selects the desired subkey of a join key
|
||||
* in 'joinkeys'
|
||||
*
|
||||
* Returns the matching path node if one exists, nil otherwise.
|
||||
*/
|
||||
static bool
|
||||
every_func(List *joinkeys, List *pathkey, int which_subkey)
|
||||
{
|
||||
JoinKey *xjoinkey;
|
||||
Var *temp;
|
||||
Var *tempkey = NULL;
|
||||
bool found = false;
|
||||
List *i = NIL;
|
||||
List *j = NIL;
|
||||
|
||||
foreach(i,joinkeys) {
|
||||
xjoinkey = (JoinKey*)lfirst(i);
|
||||
found = false;
|
||||
foreach(j,pathkey) {
|
||||
temp = (Var*)lfirst((List*)lfirst(j));
|
||||
if(temp == NULL) continue;
|
||||
tempkey = extract_subkey(xjoinkey,which_subkey);
|
||||
if(var_equal(tempkey, temp)) {
|
||||
found = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if(found == false)
|
||||
return(false);
|
||||
}
|
||||
return(found);
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* match_paths_joinkeys -
|
||||
* find the cheapest path that matches the join keys
|
||||
*/
|
||||
Path *
|
||||
match_paths_joinkeys(List *joinkeys,
|
||||
PathOrder *ordering,
|
||||
List *paths,
|
||||
int which_subkey)
|
||||
{
|
||||
Path *matched_path = NULL ;
|
||||
bool key_match = false;
|
||||
List *i = NIL;
|
||||
|
||||
foreach(i,paths) {
|
||||
Path *path = (Path*)lfirst(i);
|
||||
|
||||
key_match = every_func(joinkeys, path->keys, which_subkey);
|
||||
|
||||
if (equal_path_path_ordering(ordering,
|
||||
&path->p_ordering) &&
|
||||
length(joinkeys) == length(path->keys) &&
|
||||
key_match) {
|
||||
|
||||
if (matched_path) {
|
||||
if (path->path_cost < matched_path->path_cost)
|
||||
matched_path = path;
|
||||
} else {
|
||||
matched_path = path;
|
||||
}
|
||||
}
|
||||
}
|
||||
return matched_path;
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*
|
||||
* extract-path-keys--
|
||||
* Builds a subkey list for a path by pulling one of the subkeys from
|
||||
* a list of join keys 'joinkeys' and then finding the var node in the
|
||||
* target list 'tlist' that corresponds to that subkey.
|
||||
*
|
||||
* 'joinkeys' is a list of join key pairs
|
||||
* 'tlist' is a relation target list
|
||||
* 'which-subkey' is a flag that selects the desired subkey of a join key
|
||||
* in 'joinkeys'
|
||||
*
|
||||
* Returns a list of pathkeys: ((tlvar1)(tlvar2)...(tlvarN)).
|
||||
* [I've no idea why they have to be list of lists. Should be fixed. -ay 12/94]
|
||||
*/
|
||||
List *
|
||||
extract_path_keys(List *joinkeys,
|
||||
List *tlist,
|
||||
int which_subkey)
|
||||
{
|
||||
List *pathkeys = NIL;
|
||||
List *jk;
|
||||
|
||||
foreach(jk, joinkeys) {
|
||||
JoinKey *jkey = (JoinKey*)lfirst(jk);
|
||||
Var *var, *key;
|
||||
List *p;
|
||||
|
||||
/*
|
||||
* find the right Var in the target list for this key
|
||||
*/
|
||||
var = (Var*)extract_subkey(jkey, which_subkey);
|
||||
key = (Var*)matching_tlvar(var, tlist);
|
||||
|
||||
/*
|
||||
* include it in the pathkeys list if we haven't already done so
|
||||
*/
|
||||
foreach(p, pathkeys) {
|
||||
Var *pkey = lfirst((List*)lfirst(p)); /* XXX fix me */
|
||||
if (key == pkey)
|
||||
break;
|
||||
}
|
||||
if (p!=NIL)
|
||||
continue; /* key already in pathkeys */
|
||||
|
||||
pathkeys =
|
||||
lappend(pathkeys, lcons(key,NIL));
|
||||
}
|
||||
return(pathkeys);
|
||||
}
|
||||
|
||||
|
||||
/****************************************************************************
|
||||
* NEW PATHKEY FORMATION
|
||||
****************************************************************************/
|
||||
|
||||
/*
|
||||
* new-join-pathkeys--
|
||||
* Find the path keys for a join relation by finding all vars in the list
|
||||
* of join clauses 'joinclauses' such that:
|
||||
* (1) the var corresponding to the outer join relation is a
|
||||
* key on the outer path
|
||||
* (2) the var appears in the target list of the join relation
|
||||
* In other words, add to each outer path key the inner path keys that
|
||||
* are required for qualification.
|
||||
*
|
||||
* 'outer-pathkeys' is the list of the outer path's path keys
|
||||
* 'join-rel-tlist' is the target list of the join relation
|
||||
* 'joinclauses' is the list of restricting join clauses
|
||||
*
|
||||
* Returns the list of new path keys.
|
||||
*
|
||||
*/
|
||||
List *
|
||||
new_join_pathkeys(List *outer_pathkeys,
|
||||
List *join_rel_tlist,
|
||||
List *joinclauses)
|
||||
{
|
||||
List *outer_pathkey = NIL;
|
||||
List *t_list = NIL;
|
||||
List *x;
|
||||
List *i = NIL;
|
||||
|
||||
foreach(i, outer_pathkeys) {
|
||||
outer_pathkey = lfirst(i);
|
||||
x = new_join_pathkey(outer_pathkey, NIL,
|
||||
join_rel_tlist,joinclauses);
|
||||
if (x!=NIL) {
|
||||
t_list = lappend(t_list, x);
|
||||
}
|
||||
}
|
||||
return(t_list);
|
||||
}
|
||||
|
||||
/*
|
||||
* new-join-pathkey--
|
||||
* Finds new vars that become subkeys due to qualification clauses that
|
||||
* contain any previously considered subkeys. These new subkeys plus the
|
||||
* subkeys from 'subkeys' form a new pathkey for the join relation.
|
||||
*
|
||||
* Note that each returned subkey is the var node found in
|
||||
* 'join-rel-tlist' rather than the joinclause var node.
|
||||
*
|
||||
* 'subkeys' is a list of subkeys for which matching subkeys are to be
|
||||
* found
|
||||
* 'considered-subkeys' is the current list of all subkeys corresponding
|
||||
* to a given pathkey
|
||||
*
|
||||
* Returns a new pathkey(list of subkeys).
|
||||
*
|
||||
*/
|
||||
static List *
|
||||
new_join_pathkey(List *subkeys,
|
||||
List *considered_subkeys,
|
||||
List *join_rel_tlist,
|
||||
List *joinclauses)
|
||||
{
|
||||
List *t_list = NIL;
|
||||
Var *subkey;
|
||||
List *i = NIL;
|
||||
List *matched_subkeys = NIL;
|
||||
Expr *tlist_key = (Expr*)NULL;
|
||||
List *newly_considered_subkeys = NIL;
|
||||
|
||||
foreach (i, subkeys) {
|
||||
subkey = (Var *)lfirst(i);
|
||||
if(subkey == NULL)
|
||||
break; /* XXX something is wrong */
|
||||
matched_subkeys =
|
||||
new_matching_subkeys(subkey,considered_subkeys,
|
||||
join_rel_tlist,joinclauses);
|
||||
tlist_key = matching_tlvar(subkey,join_rel_tlist);
|
||||
newly_considered_subkeys = NIL;
|
||||
|
||||
if (tlist_key) {
|
||||
if(!member(tlist_key, matched_subkeys))
|
||||
newly_considered_subkeys = lcons(tlist_key,
|
||||
matched_subkeys);
|
||||
}
|
||||
else {
|
||||
newly_considered_subkeys = matched_subkeys;
|
||||
}
|
||||
|
||||
considered_subkeys =
|
||||
append(considered_subkeys, newly_considered_subkeys);
|
||||
|
||||
t_list = nconc(t_list,newly_considered_subkeys);
|
||||
}
|
||||
return(t_list);
|
||||
}
|
||||
|
||||
/*
|
||||
* new-matching-subkeys--
|
||||
* Returns a list of new subkeys:
|
||||
* (1) which are not listed in 'considered-subkeys'
|
||||
* (2) for which the "other" variable in some clause in 'joinclauses' is
|
||||
* 'subkey'
|
||||
* (3) which are mentioned in 'join-rel-tlist'
|
||||
*
|
||||
* Note that each returned subkey is the var node found in
|
||||
* 'join-rel-tlist' rather than the joinclause var node.
|
||||
*
|
||||
* 'subkey' is the var node for which we are trying to find matching
|
||||
* clauses
|
||||
*
|
||||
* Returns a list of new subkeys.
|
||||
*
|
||||
*/
|
||||
static List *
|
||||
new_matching_subkeys(Var *subkey,
|
||||
List *considered_subkeys,
|
||||
List *join_rel_tlist,
|
||||
List *joinclauses)
|
||||
{
|
||||
Expr *joinclause = NULL;
|
||||
List *t_list = NIL;
|
||||
List *temp = NIL;
|
||||
List *i = NIL;
|
||||
Expr *tlist_other_var = (Expr *)NULL;
|
||||
|
||||
foreach(i,joinclauses) {
|
||||
joinclause = lfirst(i);
|
||||
tlist_other_var =
|
||||
matching_tlvar(other_join_clause_var(subkey,joinclause),
|
||||
join_rel_tlist);
|
||||
|
||||
if(tlist_other_var &&
|
||||
!(member(tlist_other_var,considered_subkeys))) {
|
||||
|
||||
/* XXX was "push" function */
|
||||
considered_subkeys = lappend(considered_subkeys,
|
||||
tlist_other_var);
|
||||
|
||||
/* considered_subkeys = nreverse(considered_subkeys);
|
||||
XXX -- I am not sure of this. */
|
||||
|
||||
temp = lcons(tlist_other_var, NIL);
|
||||
t_list = nconc(t_list,temp);
|
||||
}
|
||||
}
|
||||
return(t_list);
|
||||
}
|
122
src/backend/optimizer/path/mergeutils.c
Normal file
122
src/backend/optimizer/path/mergeutils.c
Normal file
@@ -0,0 +1,122 @@
|
||||
/*-------------------------------------------------------------------------
|
||||
*
|
||||
* mergeutils.c--
|
||||
* Utilities for finding applicable merge clauses and pathkeys
|
||||
*
|
||||
* Copyright (c) 1994, Regents of the University of California
|
||||
*
|
||||
*
|
||||
* IDENTIFICATION
|
||||
* $Header: /cvsroot/pgsql/src/backend/optimizer/path/Attic/mergeutils.c,v 1.1.1.1 1996/07/09 06:21:36 scrappy Exp $
|
||||
*
|
||||
*-------------------------------------------------------------------------
|
||||
*/
|
||||
#include "postgres.h"
|
||||
|
||||
#include "nodes/pg_list.h"
|
||||
#include "nodes/relation.h"
|
||||
|
||||
#include "optimizer/internal.h"
|
||||
#include "optimizer/paths.h"
|
||||
#include "optimizer/clauses.h"
|
||||
#include "optimizer/ordering.h"
|
||||
|
||||
/*
|
||||
* group-clauses-by-order--
|
||||
* If a join clause node in 'clauseinfo-list' is mergesortable, store
|
||||
* it within a mergeinfo node containing other clause nodes with the same
|
||||
* mergesort ordering.
|
||||
*
|
||||
* 'clauseinfo-list' is the list of clauseinfo nodes
|
||||
* 'inner-relid' is the relid of the inner join relation
|
||||
*
|
||||
* Returns the new list of mergeinfo nodes.
|
||||
*
|
||||
*/
|
||||
List *
|
||||
group_clauses_by_order(List *clauseinfo_list,
|
||||
int inner_relid)
|
||||
{
|
||||
List *mergeinfo_list = NIL;
|
||||
List *xclauseinfo = NIL;
|
||||
|
||||
foreach (xclauseinfo, clauseinfo_list) {
|
||||
CInfo *clauseinfo = (CInfo *)lfirst(xclauseinfo);
|
||||
MergeOrder *merge_ordering = clauseinfo->mergesortorder;
|
||||
|
||||
if (merge_ordering) {
|
||||
/*
|
||||
* Create a new mergeinfo node and add it to
|
||||
* 'mergeinfo-list' if one does not yet exist for this
|
||||
* merge ordering.
|
||||
*/
|
||||
PathOrder p_ordering;
|
||||
MInfo *xmergeinfo;
|
||||
Expr *clause = clauseinfo->clause;
|
||||
Var *leftop = get_leftop (clause);
|
||||
Var *rightop = get_rightop (clause);
|
||||
JoinKey *keys;
|
||||
|
||||
p_ordering.ordtype = MERGE_ORDER;
|
||||
p_ordering.ord.merge = merge_ordering;
|
||||
xmergeinfo =
|
||||
match_order_mergeinfo(&p_ordering, mergeinfo_list);
|
||||
if (inner_relid == leftop->varno) {
|
||||
keys = makeNode(JoinKey);
|
||||
keys->outer = rightop;
|
||||
keys->inner = leftop;
|
||||
} else {
|
||||
keys = makeNode(JoinKey);
|
||||
keys->outer = leftop;
|
||||
keys->inner = rightop;
|
||||
}
|
||||
|
||||
if (xmergeinfo==NULL) {
|
||||
xmergeinfo = makeNode(MInfo);
|
||||
|
||||
xmergeinfo->m_ordering = merge_ordering;
|
||||
mergeinfo_list = lcons(xmergeinfo,
|
||||
mergeinfo_list);
|
||||
}
|
||||
|
||||
((JoinMethod *)xmergeinfo)->clauses =
|
||||
lcons(clause,
|
||||
((JoinMethod *)xmergeinfo)->clauses);
|
||||
((JoinMethod *)xmergeinfo)->jmkeys =
|
||||
lcons(keys,
|
||||
((JoinMethod *)xmergeinfo)->jmkeys);
|
||||
}
|
||||
}
|
||||
return(mergeinfo_list);
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* match-order-mergeinfo--
|
||||
* Searches the list 'mergeinfo-list' for a mergeinfo node whose order
|
||||
* field equals 'ordering'.
|
||||
*
|
||||
* Returns the node if it exists.
|
||||
*
|
||||
*/
|
||||
MInfo *
|
||||
match_order_mergeinfo(PathOrder *ordering, List *mergeinfo_list)
|
||||
{
|
||||
MergeOrder *xmergeorder;
|
||||
List *xmergeinfo = NIL;
|
||||
|
||||
foreach(xmergeinfo, mergeinfo_list) {
|
||||
MInfo *mergeinfo = (MInfo*)lfirst(xmergeinfo);
|
||||
|
||||
xmergeorder = mergeinfo->m_ordering;
|
||||
|
||||
if ((ordering->ordtype==MERGE_ORDER &&
|
||||
equal_merge_merge_ordering(ordering->ord.merge, xmergeorder)) ||
|
||||
(ordering->ordtype==SORTOP_ORDER &&
|
||||
equal_path_merge_ordering(ordering->ord.sortop, xmergeorder))) {
|
||||
|
||||
return (mergeinfo);
|
||||
}
|
||||
}
|
||||
return((MInfo*) NIL);
|
||||
}
|
271
src/backend/optimizer/path/orindxpath.c
Normal file
271
src/backend/optimizer/path/orindxpath.c
Normal file
@@ -0,0 +1,271 @@
|
||||
/*-------------------------------------------------------------------------
|
||||
*
|
||||
* orindxpath.c--
|
||||
* Routines to find index paths that match a set of 'or' clauses
|
||||
*
|
||||
* Copyright (c) 1994, Regents of the University of California
|
||||
*
|
||||
*
|
||||
* IDENTIFICATION
|
||||
* $Header: /cvsroot/pgsql/src/backend/optimizer/path/orindxpath.c,v 1.1.1.1 1996/07/09 06:21:36 scrappy Exp $
|
||||
*
|
||||
*-------------------------------------------------------------------------
|
||||
*/
|
||||
#include "postgres.h"
|
||||
|
||||
#include "nodes/pg_list.h"
|
||||
#include "nodes/relation.h"
|
||||
#include "nodes/primnodes.h"
|
||||
|
||||
#include "nodes/makefuncs.h"
|
||||
#include "nodes/nodeFuncs.h"
|
||||
|
||||
#include "optimizer/internal.h"
|
||||
#include "optimizer/clauses.h"
|
||||
#include "optimizer/clauseinfo.h"
|
||||
#include "optimizer/paths.h"
|
||||
#include "optimizer/cost.h"
|
||||
#include "optimizer/plancat.h"
|
||||
#include "optimizer/xfunc.h"
|
||||
|
||||
#include "parser/parsetree.h"
|
||||
|
||||
|
||||
static void best_or_subclause_indices(Query *root, Rel *rel, List *subclauses,
|
||||
List *indices, List *examined_indexids, Cost subcost, List *selectivities,
|
||||
List **indexids, Cost *cost, List **selecs);
|
||||
static void best_or_subclause_index(Query *root, Rel *rel, Expr *subclause,
|
||||
List *indices, int *indexid, Cost *cost, Cost *selec);
|
||||
|
||||
|
||||
/*
|
||||
* create-or-index-paths--
|
||||
* Creates index paths for indices that match 'or' clauses.
|
||||
*
|
||||
* 'rel' is the relation entry for which the paths are to be defined on
|
||||
* 'clauses' is the list of available restriction clause nodes
|
||||
*
|
||||
* Returns a list of these index path nodes.
|
||||
*
|
||||
*/
|
||||
List *
|
||||
create_or_index_paths(Query *root,
|
||||
Rel *rel, List *clauses)
|
||||
{
|
||||
List *t_list = NIL;
|
||||
|
||||
if (clauses != NIL) {
|
||||
CInfo *clausenode = (CInfo *) (lfirst (clauses));
|
||||
|
||||
/* Check to see if this clause is an 'or' clause, and, if so,
|
||||
* whether or not each of the subclauses within the 'or' clause has
|
||||
* been matched by an index (the 'Index field was set in
|
||||
* (match_or) if no index matches a given subclause, one of the
|
||||
* lists of index nodes returned by (get_index) will be 'nil').
|
||||
*/
|
||||
if (valid_or_clause(clausenode) &&
|
||||
clausenode->indexids) {
|
||||
List *temp = NIL;
|
||||
List *index_list = NIL;
|
||||
bool index_flag = true;
|
||||
|
||||
index_list = clausenode->indexids;
|
||||
foreach(temp,index_list) {
|
||||
if (!temp)
|
||||
index_flag = false;
|
||||
}
|
||||
if (index_flag) { /* used to be a lisp every function */
|
||||
IndexPath *pathnode = makeNode(IndexPath);
|
||||
List *indexids;
|
||||
Cost cost;
|
||||
List *selecs;
|
||||
|
||||
best_or_subclause_indices(root,
|
||||
rel,
|
||||
clausenode->clause->args,
|
||||
clausenode->indexids,
|
||||
NIL,
|
||||
(Cost)0,
|
||||
NIL,
|
||||
&indexids,
|
||||
&cost,
|
||||
&selecs);
|
||||
|
||||
pathnode->path.pathtype = T_IndexScan;
|
||||
pathnode->path.parent = rel;
|
||||
pathnode->indexqual =
|
||||
lcons(clausenode,NIL);
|
||||
pathnode->indexid = indexids;
|
||||
pathnode->path.path_cost = cost;
|
||||
|
||||
/* copy clauseinfo list into path for expensive
|
||||
function processing -- JMH, 7/7/92 */
|
||||
pathnode->path.locclauseinfo =
|
||||
set_difference(clauses,
|
||||
copyObject((Node*)
|
||||
rel->clauseinfo));
|
||||
|
||||
#if 0 /* fix xfunc */
|
||||
/* add in cost for expensive functions! -- JMH, 7/7/92 */
|
||||
if (XfuncMode != XFUNC_OFF) {
|
||||
((Path*)pathnode)->path_cost +=
|
||||
xfunc_get_path_cost((Path)pathnode);
|
||||
}
|
||||
#endif
|
||||
clausenode->selectivity = (Cost)floatVal(selecs);
|
||||
t_list =
|
||||
lcons(pathnode,
|
||||
create_or_index_paths(root, rel,lnext(clauses)));
|
||||
} else {
|
||||
t_list = create_or_index_paths(root, rel,lnext(clauses));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return(t_list);
|
||||
}
|
||||
|
||||
/*
|
||||
* best-or-subclause-indices--
|
||||
* Determines the best index to be used in conjunction with each subclause
|
||||
* of an 'or' clause and the cost of scanning a relation using these
|
||||
* indices. The cost is the sum of the individual index costs.
|
||||
*
|
||||
* 'rel' is the node of the relation on which the index is defined
|
||||
* 'subclauses' are the subclauses of the 'or' clause
|
||||
* 'indices' are those index nodes that matched subclauses of the 'or'
|
||||
* clause
|
||||
* 'examined-indexids' is a list of those index ids to be used with
|
||||
* subclauses that have already been examined
|
||||
* 'subcost' is the cost of using the indices in 'examined-indexids'
|
||||
* 'selectivities' is a list of the selectivities of subclauses that
|
||||
* have already been examined
|
||||
*
|
||||
* Returns a list of the indexids, cost, and selectivities of each
|
||||
* subclause, e.g., ((i1 i2 i3) cost (s1 s2 s3)), where 'i' is an OID,
|
||||
* 'cost' is a flonum, and 's' is a flonum.
|
||||
*/
|
||||
static void
|
||||
best_or_subclause_indices(Query *root,
|
||||
Rel *rel,
|
||||
List *subclauses,
|
||||
List *indices,
|
||||
List *examined_indexids,
|
||||
Cost subcost,
|
||||
List *selectivities,
|
||||
List **indexids, /* return value */
|
||||
Cost *cost, /* return value */
|
||||
List **selecs) /* return value */
|
||||
{
|
||||
if (subclauses==NIL) {
|
||||
*indexids = nreverse(examined_indexids);
|
||||
*cost = subcost;
|
||||
*selecs = nreverse(selectivities);
|
||||
} else {
|
||||
int best_indexid;
|
||||
Cost best_cost;
|
||||
Cost best_selec;
|
||||
|
||||
best_or_subclause_index(root, rel, lfirst(subclauses), lfirst(indices),
|
||||
&best_indexid, &best_cost, &best_selec);
|
||||
|
||||
best_or_subclause_indices(root,
|
||||
rel,
|
||||
lnext(subclauses),
|
||||
lnext(indices),
|
||||
lconsi(best_indexid, examined_indexids),
|
||||
subcost + best_cost,
|
||||
lcons(makeFloat(best_selec), selectivities),
|
||||
indexids,
|
||||
cost,
|
||||
selecs);
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
/*
|
||||
* best-or-subclause-index--
|
||||
* Determines which is the best index to be used with a subclause of
|
||||
* an 'or' clause by estimating the cost of using each index and selecting
|
||||
* the least expensive.
|
||||
*
|
||||
* 'rel' is the node of the relation on which the index is defined
|
||||
* 'subclause' is the subclause
|
||||
* 'indices' is a list of index nodes that match the subclause
|
||||
*
|
||||
* Returns a list (index-id index-subcost index-selectivity)
|
||||
* (a fixnum, a fixnum, and a flonum respectively).
|
||||
*
|
||||
*/
|
||||
static void
|
||||
best_or_subclause_index(Query *root,
|
||||
Rel *rel,
|
||||
Expr *subclause,
|
||||
List *indices,
|
||||
int *retIndexid, /* return value */
|
||||
Cost *retCost, /* return value */
|
||||
Cost *retSelec) /* return value */
|
||||
{
|
||||
if (indices != NIL) {
|
||||
Datum value;
|
||||
int flag = 0;
|
||||
Cost subcost;
|
||||
Rel *index = (Rel *)lfirst (indices);
|
||||
AttrNumber attno = (get_leftop (subclause))->varattno ;
|
||||
Oid opno = ((Oper*)subclause->oper)->opno;
|
||||
bool constant_on_right = non_null((Expr*)get_rightop(subclause));
|
||||
float npages, selec;
|
||||
int subclause_indexid;
|
||||
Cost subclause_cost;
|
||||
Cost subclause_selec;
|
||||
|
||||
if(constant_on_right) {
|
||||
value = ((Const*)get_rightop (subclause))->constvalue;
|
||||
} else {
|
||||
value = NameGetDatum("");
|
||||
}
|
||||
if(constant_on_right) {
|
||||
flag = (_SELEC_IS_CONSTANT_ ||_SELEC_CONSTANT_RIGHT_);
|
||||
} else {
|
||||
flag = _SELEC_CONSTANT_RIGHT_;
|
||||
}
|
||||
index_selectivity(lfirsti(index->relids),
|
||||
index->classlist,
|
||||
lconsi(opno,NIL),
|
||||
getrelid(lfirsti(rel->relids),
|
||||
root->rtable),
|
||||
lconsi(attno,NIL),
|
||||
lconsi(value,NIL),
|
||||
lconsi(flag,NIL),
|
||||
1,
|
||||
&npages,
|
||||
&selec);
|
||||
|
||||
subcost = cost_index((Oid) lfirsti(index->relids),
|
||||
(int)npages,
|
||||
(Cost)selec,
|
||||
rel->pages,
|
||||
rel->tuples,
|
||||
index->pages,
|
||||
index->tuples,
|
||||
false);
|
||||
best_or_subclause_index(root,
|
||||
rel,
|
||||
subclause,
|
||||
lnext(indices),
|
||||
&subclause_indexid,
|
||||
&subclause_cost,
|
||||
&subclause_selec);
|
||||
|
||||
if (subclause_indexid==0 || subcost < subclause_cost) {
|
||||
*retIndexid = lfirsti(index->relids);
|
||||
*retCost = subcost;
|
||||
*retSelec = selec;
|
||||
} else {
|
||||
*retIndexid = 0;
|
||||
*retCost = 0.0;
|
||||
*retSelec = 0.0;
|
||||
}
|
||||
}
|
||||
return;
|
||||
}
|
773
src/backend/optimizer/path/predmig.c
Normal file
773
src/backend/optimizer/path/predmig.c
Normal file
@@ -0,0 +1,773 @@
|
||||
/*-------------------------------------------------------------------------
|
||||
*
|
||||
* predmig.c--
|
||||
*
|
||||
*
|
||||
* Copyright (c) 1994, Regents of the University of California
|
||||
*
|
||||
*
|
||||
* IDENTIFICATION
|
||||
* $Header: /cvsroot/pgsql/src/backend/optimizer/path/Attic/predmig.c,v 1.1.1.1 1996/07/09 06:21:36 scrappy Exp $
|
||||
*
|
||||
*-------------------------------------------------------------------------
|
||||
*/
|
||||
/*
|
||||
** DESCRIPTION
|
||||
** Main Routines to handle Predicate Migration (i.e. correct optimization
|
||||
** of queries with expensive functions.)
|
||||
**
|
||||
** The reasoning behind some of these algorithms is rather detailed.
|
||||
** Have a look at Sequoia Tech Report 92/13 for more info. Also
|
||||
** see Monma and Sidney's paper "Sequencing with Series-Parallel
|
||||
** Precedence Constraints", in "Mathematics of Operations Research",
|
||||
** volume 4 (1979), pp. 215-224.
|
||||
**
|
||||
** The main thing that this code does that wasn't handled in xfunc.c is
|
||||
** it considers the possibility that two joins in a stream may not
|
||||
** be ordered by ascending rank -- in such a scenario, it may be optimal
|
||||
** to pullup more restrictions than we did via xfunc_try_pullup.
|
||||
**
|
||||
** This code in some sense generalizes xfunc_try_pullup; if you
|
||||
** run postgres -x noprune, you'll turn off xfunc_try_pullup, and this
|
||||
** code will do everything that xfunc_try_pullup would have, and maybe
|
||||
** more. However, this results in no pruning, which may slow down the
|
||||
** optimizer and/or cause the system to run out of memory.
|
||||
** -- JMH, 11/13/92
|
||||
*/
|
||||
|
||||
#include "nodes/pg_list.h"
|
||||
#include "nodes/nodes.h"
|
||||
#include "nodes/primnodes.h"
|
||||
#include "nodes/relation.h"
|
||||
#include "utils/palloc.h"
|
||||
#include "utils/elog.h"
|
||||
#include "planner/xfunc.h"
|
||||
#include "planner/pathnode.h"
|
||||
#include "planner/internal.h"
|
||||
#include "planner/cost.h"
|
||||
#include "planner/keys.h"
|
||||
#include "planner/tlist.h"
|
||||
#include "lib/qsort.h"
|
||||
|
||||
#define is_clause(node) (get_cinfo(node)) /* a stream node represents a
|
||||
clause (not a join) iff it
|
||||
has a non-NULL cinfo field */
|
||||
|
||||
static void xfunc_predmig(JoinPath pathnode, Stream streamroot,
|
||||
Stream laststream, bool *progressp);
|
||||
static bool xfunc_series_llel(Stream stream);
|
||||
static bool xfunc_llel_chains(Stream root, Stream bottom);
|
||||
static Stream xfunc_complete_stream(Stream stream);
|
||||
static bool xfunc_prdmig_pullup(Stream origstream, Stream pullme,
|
||||
JoinPath joinpath);
|
||||
static void xfunc_form_groups(Stream root, Stream bottom);
|
||||
static void xfunc_free_stream(Stream root);
|
||||
static Stream xfunc_add_clauses(Stream current);
|
||||
static void xfunc_setup_group(Stream node, Stream bottom);
|
||||
static Stream xfunc_streaminsert(CInfo clauseinfo, Stream current,
|
||||
int clausetype);
|
||||
static int xfunc_num_relids(Stream node);
|
||||
static StreamPtr xfunc_get_downjoin(Stream node);
|
||||
static StreamPtr xfunc_get_upjoin(Stream node);
|
||||
static Stream xfunc_stream_qsort(Stream root, Stream bottom);
|
||||
static int xfunc_stream_compare(void *arg1, void *arg2);
|
||||
static bool xfunc_check_stream(Stream node);
|
||||
static bool xfunc_in_stream(Stream node, Stream stream);
|
||||
|
||||
/* ----------------- MAIN FUNCTIONS ------------------------ */
|
||||
/*
|
||||
** xfunc_do_predmig
|
||||
** wrapper for Predicate Migration. It calls xfunc_predmig until no
|
||||
** more progress is made.
|
||||
** return value says if any changes were ever made.
|
||||
*/
|
||||
bool xfunc_do_predmig(Path root)
|
||||
{
|
||||
bool progress, changed = false;
|
||||
|
||||
if (is_join(root))
|
||||
do
|
||||
{
|
||||
progress = false;
|
||||
Assert(IsA(root,JoinPath));
|
||||
xfunc_predmig((JoinPath)root, (Stream)NULL, (Stream)NULL,
|
||||
&progress);
|
||||
if (changed && progress)
|
||||
elog(DEBUG, "Needed to do a second round of predmig!\n");
|
||||
if (progress) changed = true;
|
||||
} while (progress);
|
||||
return(changed);
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
** xfunc_predmig
|
||||
** The main routine for Predicate Migration. It traverses a join tree,
|
||||
** and for each root-to-leaf path in the plan tree it constructs a
|
||||
** "Stream", which it passes to xfunc_series_llel for optimization.
|
||||
** Destructively modifies the join tree (via predicate pullup).
|
||||
*/
|
||||
static void
|
||||
xfunc_predmig(JoinPath pathnode, /* root of the join tree */
|
||||
Stream streamroot,
|
||||
Stream laststream, /* for recursive calls -- these are
|
||||
the root of the stream under
|
||||
construction, and the lowest node
|
||||
created so far */
|
||||
bool *progressp)
|
||||
{
|
||||
Stream newstream;
|
||||
|
||||
/*
|
||||
** traverse the join tree dfs-style, constructing a stream as you go.
|
||||
** When you hit a scan node, pass the stream off to xfunc_series_llel.
|
||||
*/
|
||||
|
||||
/* sanity check */
|
||||
if ((!streamroot && laststream) ||
|
||||
(streamroot && !laststream))
|
||||
elog(WARN, "called xfunc_predmig with bad inputs");
|
||||
if (streamroot) Assert(xfunc_check_stream(streamroot));
|
||||
|
||||
/* add path node to stream */
|
||||
newstream = RMakeStream();
|
||||
if (!streamroot)
|
||||
streamroot = newstream;
|
||||
set_upstream(newstream, (StreamPtr)laststream);
|
||||
if (laststream)
|
||||
set_downstream(laststream, (StreamPtr)newstream);
|
||||
set_downstream(newstream, (StreamPtr)NULL);
|
||||
set_pathptr(newstream, (pathPtr)pathnode);
|
||||
set_cinfo(newstream, (CInfo)NULL);
|
||||
set_clausetype(newstream, XFUNC_UNKNOWN);
|
||||
|
||||
/* base case: we're at a leaf, call xfunc_series_llel */
|
||||
if (!is_join(pathnode))
|
||||
{
|
||||
/* form a fleshed-out copy of the stream */
|
||||
Stream fullstream = xfunc_complete_stream(streamroot);
|
||||
|
||||
/* sort it via series-llel */
|
||||
if (xfunc_series_llel(fullstream))
|
||||
*progressp = true;
|
||||
|
||||
/* free up the copy */
|
||||
xfunc_free_stream(fullstream);
|
||||
}
|
||||
else
|
||||
{
|
||||
/* visit left child */
|
||||
xfunc_predmig((JoinPath)get_outerjoinpath(pathnode),
|
||||
streamroot, newstream, progressp);
|
||||
|
||||
/* visit right child */
|
||||
xfunc_predmig((JoinPath)get_innerjoinpath(pathnode),
|
||||
streamroot, newstream, progressp);
|
||||
}
|
||||
|
||||
/* remove this node */
|
||||
if (get_upstream(newstream))
|
||||
set_downstream((Stream)get_upstream(newstream), (StreamPtr)NULL);
|
||||
pfree(newstream);
|
||||
}
|
||||
|
||||
/*
|
||||
** xfunc_series_llel
|
||||
** A flavor of Monma and Sidney's Series-Parallel algorithm.
|
||||
** Traverse stream downwards. When you find a node with restrictions on it,
|
||||
** call xfunc_llel_chains on the substream from root to that node.
|
||||
*/
|
||||
static bool xfunc_series_llel(Stream stream)
|
||||
{
|
||||
Stream temp, next;
|
||||
bool progress = false;
|
||||
|
||||
for (temp = stream; temp != (Stream)NULL; temp = next)
|
||||
{
|
||||
next = (Stream)xfunc_get_downjoin(temp);
|
||||
/*
|
||||
** if there are restrictions/secondary join clauses above this
|
||||
** node, call xfunc_llel_chains
|
||||
*/
|
||||
if (get_upstream(temp) && is_clause((Stream)get_upstream(temp)))
|
||||
if (xfunc_llel_chains(stream, temp))
|
||||
progress = true;
|
||||
}
|
||||
return(progress);
|
||||
}
|
||||
|
||||
/*
|
||||
** xfunc_llel_chains
|
||||
** A flavor of Monma and Sidney's Parallel Chains algorithm.
|
||||
** Given a stream which has been well-ordered except for its lowermost
|
||||
** restrictions/2-ary joins, pull up the restrictions/2-arys as appropriate.
|
||||
** What that means here is to form groups in the chain above the lowest
|
||||
** join node above bottom inclusive, and then take all the restrictions
|
||||
** following bottom, and try to pull them up as far as possible.
|
||||
*/
|
||||
static bool xfunc_llel_chains(Stream root, Stream bottom)
|
||||
{
|
||||
bool progress = false;
|
||||
Stream origstream;
|
||||
Stream tmpstream, pathstream;
|
||||
Stream rootcopy = root;
|
||||
|
||||
Assert(xfunc_check_stream(root));
|
||||
|
||||
/* xfunc_prdmig_pullup will need an unmodified copy of the stream */
|
||||
origstream = (Stream)copyObject((Node)root);
|
||||
|
||||
/* form groups among ill-ordered nodes */
|
||||
xfunc_form_groups(root, bottom);
|
||||
|
||||
/* sort chain by rank */
|
||||
Assert(xfunc_in_stream(bottom, root));
|
||||
rootcopy = xfunc_stream_qsort(root, bottom);
|
||||
|
||||
/*
|
||||
** traverse sorted stream -- if any restriction has moved above a join,
|
||||
** we must pull it up in the plan. That is, make plan tree
|
||||
** reflect order of sorted stream.
|
||||
*/
|
||||
for (tmpstream = rootcopy,
|
||||
pathstream = (Stream)xfunc_get_downjoin(rootcopy);
|
||||
tmpstream != (Stream)NULL && pathstream != (Stream)NULL;
|
||||
tmpstream = (Stream)get_downstream(tmpstream))
|
||||
{
|
||||
if (is_clause(tmpstream)
|
||||
&& get_pathptr(pathstream) != get_pathptr(tmpstream))
|
||||
{
|
||||
/*
|
||||
** If restriction moved above a Join after sort, we pull it
|
||||
** up in the join plan.
|
||||
** If restriction moved down, we ignore it.
|
||||
** This is because Joey's Sequoia paper proves that
|
||||
** restrictions should never move down. If this
|
||||
** one were moved down, it would violate "semantic correctness",
|
||||
** i.e. it would be lower than the attributes it references.
|
||||
*/
|
||||
Assert(xfunc_num_relids(pathstream)>xfunc_num_relids(tmpstream));
|
||||
progress =
|
||||
xfunc_prdmig_pullup(origstream, tmpstream,
|
||||
(JoinPath)get_pathptr(pathstream));
|
||||
}
|
||||
if (get_downstream(tmpstream))
|
||||
pathstream =
|
||||
(Stream)xfunc_get_downjoin((Stream)get_downstream(tmpstream));
|
||||
}
|
||||
|
||||
/* free up origstream */
|
||||
xfunc_free_stream(origstream);
|
||||
return(progress);
|
||||
}
|
||||
|
||||
/*
|
||||
** xfunc_complete_stream --
|
||||
** Given a stream composed of join nodes only, make a copy containing the
|
||||
** join nodes along with the associated restriction nodes.
|
||||
*/
|
||||
static Stream xfunc_complete_stream(Stream stream)
|
||||
{
|
||||
Stream tmpstream, copystream, curstream = (Stream)NULL;
|
||||
|
||||
copystream = (Stream)copyObject((Node)stream);
|
||||
Assert(xfunc_check_stream(copystream));
|
||||
|
||||
curstream = copystream;
|
||||
Assert(!is_clause(curstream));
|
||||
|
||||
/* curstream = (Stream)xfunc_get_downjoin(curstream); */
|
||||
|
||||
while(curstream != (Stream)NULL)
|
||||
{
|
||||
xfunc_add_clauses(curstream);
|
||||
curstream = (Stream)xfunc_get_downjoin(curstream);
|
||||
}
|
||||
|
||||
/* find top of stream and return it */
|
||||
for (tmpstream = copystream; get_upstream(tmpstream) != (StreamPtr)NULL;
|
||||
tmpstream = (Stream)get_upstream(tmpstream))
|
||||
/* no body in for loop */;
|
||||
|
||||
return(tmpstream);
|
||||
}
|
||||
|
||||
/*
|
||||
** xfunc_prdmig_pullup
|
||||
** pullup a clause in a path above joinpath. Since the JoinPath tree
|
||||
** doesn't have upward pointers, it's difficult to deal with. Thus we
|
||||
** require the original stream, which maintains pointers to all the path
|
||||
** nodes. We use the original stream to find out what joins are
|
||||
** above the clause.
|
||||
*/
|
||||
static bool
|
||||
xfunc_prdmig_pullup(Stream origstream, Stream pullme, JoinPath joinpath)
|
||||
{
|
||||
CInfo clauseinfo = get_cinfo(pullme);
|
||||
bool progress = false;
|
||||
Stream upjoin, orignode, temp;
|
||||
int whichchild;
|
||||
|
||||
/* find node in origstream that contains clause */
|
||||
for (orignode = origstream;
|
||||
orignode != (Stream) NULL
|
||||
&& get_cinfo(orignode) != clauseinfo;
|
||||
orignode = (Stream)get_downstream(orignode))
|
||||
/* empty body in for loop */ ;
|
||||
if (!orignode)
|
||||
elog(WARN, "Didn't find matching node in original stream");
|
||||
|
||||
|
||||
/* pull up this node as far as it should go */
|
||||
for (upjoin = (Stream)xfunc_get_upjoin(orignode);
|
||||
upjoin != (Stream)NULL
|
||||
&& (JoinPath)get_pathptr((Stream)xfunc_get_downjoin(upjoin))
|
||||
!= joinpath;
|
||||
upjoin = (Stream)xfunc_get_upjoin(upjoin))
|
||||
{
|
||||
#ifdef DEBUG
|
||||
elog(DEBUG, "pulling up in xfunc_predmig_pullup!");
|
||||
#endif
|
||||
/* move clause up in path */
|
||||
if (get_pathptr((Stream)get_downstream(upjoin))
|
||||
== (pathPtr)get_outerjoinpath((JoinPath)get_pathptr(upjoin)))
|
||||
whichchild = OUTER;
|
||||
else whichchild = INNER;
|
||||
clauseinfo = xfunc_pullup((Path)get_pathptr((Stream)get_downstream(upjoin)),
|
||||
(JoinPath)get_pathptr(upjoin),
|
||||
clauseinfo,
|
||||
whichchild,
|
||||
get_clausetype(orignode));
|
||||
set_pathptr(pullme, get_pathptr(upjoin));
|
||||
/* pullme has been moved into locclauseinfo */
|
||||
set_clausetype(pullme, XFUNC_LOCPRD);
|
||||
|
||||
/*
|
||||
** xfunc_pullup makes new path nodes for children of
|
||||
** get_pathptr(current). We must modify the stream nodes to point
|
||||
** to these path nodes
|
||||
*/
|
||||
if (whichchild == OUTER)
|
||||
{
|
||||
for(temp = (Stream)get_downstream(upjoin); is_clause(temp);
|
||||
temp = (Stream)get_downstream(temp))
|
||||
set_pathptr
|
||||
(temp, (pathPtr)
|
||||
get_outerjoinpath((JoinPath)get_pathptr(upjoin)));
|
||||
set_pathptr
|
||||
(temp,
|
||||
(pathPtr)get_outerjoinpath((JoinPath)get_pathptr(upjoin)));
|
||||
}
|
||||
else
|
||||
{
|
||||
for(temp = (Stream)get_downstream(upjoin); is_clause(temp);
|
||||
temp = (Stream)get_downstream(temp))
|
||||
set_pathptr
|
||||
(temp, (pathPtr)
|
||||
get_innerjoinpath((JoinPath)get_pathptr(upjoin)));
|
||||
set_pathptr
|
||||
(temp, (pathPtr)
|
||||
get_innerjoinpath((JoinPath)get_pathptr(upjoin)));
|
||||
}
|
||||
progress = true;
|
||||
}
|
||||
if (!progress)
|
||||
elog(DEBUG, "didn't succeed in pulling up in xfunc_prdmig_pullup");
|
||||
return(progress);
|
||||
}
|
||||
|
||||
/*
|
||||
** xfunc_form_groups --
|
||||
** A group is a pair of stream nodes a,b such that a is constrained to
|
||||
** precede b (for instance if a and b are both joins), but rank(a) > rank(b).
|
||||
** In such a situation, Monma and Sidney prove that no clauses should end
|
||||
** up between a and b, and therefore we may treat them as a group, with
|
||||
** selectivity equal to the product of their selectivities, and cost
|
||||
** equal to the cost of the first plus the selectivity of the first times the
|
||||
** cost of the second. We define each node to be in a group by itself,
|
||||
** and then repeatedly find adjacent groups which are ordered by descending
|
||||
** rank, and make larger groups. You know that two adjacent nodes are in a
|
||||
** group together if the lower has groupup set to true. They will both have
|
||||
** the same groupcost and groupsel (since they're in the same group!)
|
||||
*/
|
||||
static void xfunc_form_groups(Query* queryInfo, Stream root, Stream bottom)
|
||||
{
|
||||
Stream temp, parent;
|
||||
int lowest = xfunc_num_relids((Stream)xfunc_get_upjoin(bottom));
|
||||
bool progress;
|
||||
LispValue primjoin;
|
||||
int whichchild;
|
||||
|
||||
if (!lowest) return; /* no joins in stream, so no groups */
|
||||
|
||||
/* initialize groups to be single nodes */
|
||||
for (temp = root;
|
||||
temp != (Stream)NULL && temp != bottom;
|
||||
temp = (Stream)get_downstream(temp))
|
||||
{
|
||||
/* if a Join node */
|
||||
if (!is_clause(temp))
|
||||
{
|
||||
if (get_pathptr((Stream)get_downstream(temp))
|
||||
== (pathPtr)get_outerjoinpath((JoinPath)get_pathptr(temp)))
|
||||
whichchild = OUTER;
|
||||
else whichchild = INNER;
|
||||
set_groupcost(temp,
|
||||
xfunc_join_expense((JoinPath)get_pathptr(temp),
|
||||
whichchild));
|
||||
if (primjoin = xfunc_primary_join((JoinPath)get_pathptr(temp)))
|
||||
{
|
||||
set_groupsel(temp,
|
||||
compute_clause_selec(queryInfo,
|
||||
primjoin, NIL));
|
||||
}
|
||||
else
|
||||
{
|
||||
set_groupsel(temp,1.0);
|
||||
}
|
||||
}
|
||||
else /* a restriction, or 2-ary join pred */
|
||||
{
|
||||
set_groupcost(temp,
|
||||
xfunc_expense(queryInfo,
|
||||
get_clause(get_cinfo(temp))));
|
||||
set_groupsel(temp,
|
||||
compute_clause_selec(queryInfo,
|
||||
get_clause(get_cinfo(temp)),
|
||||
NIL));
|
||||
}
|
||||
set_groupup(temp,false);
|
||||
}
|
||||
|
||||
/* make passes upwards, forming groups */
|
||||
do
|
||||
{
|
||||
progress = false;
|
||||
for (temp = (Stream)get_upstream(bottom);
|
||||
temp != (Stream)NULL;
|
||||
temp = (Stream)get_upstream(temp))
|
||||
{
|
||||
/* check for grouping with node upstream */
|
||||
if (!get_groupup(temp) && /* not already grouped */
|
||||
(parent = (Stream)get_upstream(temp)) != (Stream)NULL &&
|
||||
/* temp is a join or temp is the top of a group */
|
||||
(is_join((Path)get_pathptr(temp)) ||
|
||||
get_downstream(temp) &&
|
||||
get_groupup((Stream)get_downstream(temp))) &&
|
||||
get_grouprank(parent) < get_grouprank(temp))
|
||||
{
|
||||
progress = true; /* we formed a new group */
|
||||
set_groupup(temp,true);
|
||||
set_groupcost(temp,
|
||||
get_groupcost(temp) +
|
||||
get_groupsel(temp) * get_groupcost(parent));
|
||||
set_groupsel(temp,get_groupsel(temp) * get_groupsel(parent));
|
||||
|
||||
/* fix costs and sels of all members of group */
|
||||
xfunc_setup_group(temp, bottom);
|
||||
}
|
||||
}
|
||||
} while(progress);
|
||||
}
|
||||
|
||||
|
||||
/* ------------------- UTILITY FUNCTIONS ------------------------- */
|
||||
|
||||
/*
|
||||
** xfunc_free_stream --
|
||||
** walk down a stream and pfree it
|
||||
*/
|
||||
static void xfunc_free_stream(Stream root)
|
||||
{
|
||||
Stream cur, next;
|
||||
|
||||
Assert(xfunc_check_stream(root));
|
||||
|
||||
if (root != (Stream)NULL)
|
||||
for (cur = root; cur != (Stream)NULL; cur = next)
|
||||
{
|
||||
next = (Stream)get_downstream(cur);
|
||||
pfree(cur);
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
** xfunc_add<_clauses
|
||||
** find any clauses above current, and insert them into stream as
|
||||
** appropriate. Return uppermost clause inserted, or current if none.
|
||||
*/
|
||||
static Stream xfunc_add_clauses(Stream current)
|
||||
{
|
||||
Stream topnode = current;
|
||||
LispValue temp;
|
||||
LispValue primjoin;
|
||||
|
||||
/* first add in the local clauses */
|
||||
foreach(temp, get_locclauseinfo((Path)get_pathptr(current)))
|
||||
{
|
||||
topnode =
|
||||
xfunc_streaminsert((CInfo)lfirst(temp), topnode,
|
||||
XFUNC_LOCPRD);
|
||||
}
|
||||
|
||||
/* and add in the join clauses */
|
||||
if (IsA(get_pathptr(current),JoinPath))
|
||||
{
|
||||
primjoin = xfunc_primary_join((JoinPath)get_pathptr(current));
|
||||
foreach(temp, get_pathclauseinfo((JoinPath)get_pathptr(current)))
|
||||
{
|
||||
if (!equal(get_clause((CInfo)lfirst(temp)), primjoin))
|
||||
topnode =
|
||||
xfunc_streaminsert((CInfo)lfirst(temp), topnode,
|
||||
XFUNC_JOINPRD);
|
||||
}
|
||||
}
|
||||
return(topnode);
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
** xfunc_setup_group
|
||||
** find all elements of stream that are grouped with node and are above
|
||||
** bottom, and set their groupcost and groupsel to be the same as node's.
|
||||
*/
|
||||
static void xfunc_setup_group(Stream node, Stream bottom)
|
||||
{
|
||||
Stream temp;
|
||||
|
||||
if (node != bottom)
|
||||
/* traverse downwards */
|
||||
for (temp = (Stream)get_downstream(node);
|
||||
temp != (Stream)NULL && temp != bottom;
|
||||
temp = (Stream)get_downstream(temp))
|
||||
{
|
||||
if (!get_groupup(temp)) break;
|
||||
else
|
||||
{
|
||||
set_groupcost(temp, get_groupcost(node));
|
||||
set_groupsel(temp, get_groupsel(node));
|
||||
}
|
||||
}
|
||||
|
||||
/* traverse upwards */
|
||||
for (temp = (Stream)get_upstream(node); temp != (Stream)NULL;
|
||||
temp = (Stream)get_upstream(temp))
|
||||
{
|
||||
if (!get_groupup((Stream)get_downstream(temp))) break;
|
||||
else
|
||||
{
|
||||
set_groupcost(temp, get_groupcost(node));
|
||||
set_groupsel(temp, get_groupsel(node));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
** xfunc_streaminsert
|
||||
** Make a new Stream node to hold clause, and insert it above current.
|
||||
** Return new node.
|
||||
*/
|
||||
static Stream
|
||||
xfunc_streaminsert(CInfo clauseinfo,
|
||||
Stream current,
|
||||
int clausetype) /* XFUNC_LOCPRD or XFUNC_JOINPRD */
|
||||
{
|
||||
Stream newstream = RMakeStream();
|
||||
set_upstream(newstream, get_upstream(current));
|
||||
if (get_upstream(current))
|
||||
set_downstream((Stream)(get_upstream(current)), (StreamPtr)newstream);
|
||||
set_upstream(current, (StreamPtr)newstream);
|
||||
set_downstream(newstream, (StreamPtr)current);
|
||||
set_pathptr(newstream, get_pathptr(current));
|
||||
set_cinfo(newstream, clauseinfo);
|
||||
set_clausetype(newstream, clausetype);
|
||||
return(newstream);
|
||||
}
|
||||
|
||||
/*
|
||||
** Given a Stream node, find the number of relids referenced in the pathnode
|
||||
** associated with the stream node. The number of relids gives a unique
|
||||
** ordering on the joins in a stream, which we use to compare the height of
|
||||
** join nodes.
|
||||
*/
|
||||
static int xfunc_num_relids(Stream node)
|
||||
{
|
||||
if (!node || !IsA(get_pathptr(node),JoinPath))
|
||||
return(0);
|
||||
else return(length
|
||||
(get_relids(get_parent((JoinPath)get_pathptr(node)))));
|
||||
}
|
||||
|
||||
/*
|
||||
** xfunc_get_downjoin --
|
||||
** Given a stream node, find the next lowest node which points to a
|
||||
** join predicate or a scan node.
|
||||
*/
|
||||
static StreamPtr xfunc_get_downjoin(Stream node)
|
||||
{
|
||||
Stream temp;
|
||||
|
||||
if (!is_clause(node)) /* if this is a join */
|
||||
node = (Stream)get_downstream(node);
|
||||
for (temp = node; temp && is_clause(temp);
|
||||
temp = (Stream)get_downstream(temp))
|
||||
/* empty body in for loop */ ;
|
||||
|
||||
return((StreamPtr)temp);
|
||||
}
|
||||
|
||||
/*
|
||||
** xfunc_get_upjoin --
|
||||
** same as above, but upwards.
|
||||
*/
|
||||
static StreamPtr xfunc_get_upjoin(Stream node)
|
||||
{
|
||||
Stream temp;
|
||||
|
||||
if (!is_clause(node)) /* if this is a join */
|
||||
node = (Stream)get_upstream(node);
|
||||
for (temp = node; temp && is_clause(temp);
|
||||
temp = (Stream)get_upstream(temp))
|
||||
/* empty body in for loop */ ;
|
||||
|
||||
return((StreamPtr)temp);
|
||||
}
|
||||
|
||||
/*
|
||||
** xfunc_stream_qsort --
|
||||
** Given a stream, sort by group rank the elements in the stream from the
|
||||
** node "bottom" up. DESTRUCTIVELY MODIFIES STREAM! Returns new root.
|
||||
*/
|
||||
static Stream xfunc_stream_qsort(Stream root, Stream bottom)
|
||||
{
|
||||
int i;
|
||||
size_t num;
|
||||
Stream *nodearray, output;
|
||||
Stream tmp;
|
||||
|
||||
/* find size of list */
|
||||
for (num = 0, tmp = root; tmp != bottom;
|
||||
tmp = (Stream)get_downstream(tmp))
|
||||
num ++;
|
||||
if (num <= 1) return (root);
|
||||
|
||||
/* copy elements of the list into an array */
|
||||
nodearray = (Stream *) palloc(num * sizeof(Stream));
|
||||
|
||||
for (tmp = root, i = 0; tmp != bottom;
|
||||
tmp = (Stream)get_downstream(tmp), i++)
|
||||
nodearray[i] = tmp;
|
||||
|
||||
/* sort the array */
|
||||
pg_qsort(nodearray, num, sizeof(LispValue), xfunc_stream_compare);
|
||||
|
||||
/* paste together the array elements */
|
||||
output = nodearray[num - 1];
|
||||
set_upstream(output, (StreamPtr)NULL);
|
||||
for (i = num - 2; i >= 0; i--)
|
||||
{
|
||||
set_downstream(nodearray[i+1], (StreamPtr)nodearray[i]);
|
||||
set_upstream(nodearray[i], (StreamPtr)nodearray[i+1]);
|
||||
}
|
||||
set_downstream(nodearray[0], (StreamPtr)bottom);
|
||||
if (bottom)
|
||||
set_upstream(bottom, (StreamPtr)nodearray[0]);
|
||||
|
||||
Assert(xfunc_check_stream(output));
|
||||
return(output);
|
||||
}
|
||||
|
||||
/*
|
||||
** xfunc_stream_compare
|
||||
** comparison function for xfunc_stream_qsort.
|
||||
** Compare nodes by group rank. If group ranks are equal, ensure that
|
||||
** join nodes appear in same order as in plan tree.
|
||||
*/
|
||||
static int xfunc_stream_compare(void *arg1, void *arg2)
|
||||
{
|
||||
Stream stream1 = *(Stream *) arg1;
|
||||
Stream stream2 = *(Stream *) arg2;
|
||||
Cost rank1, rank2;
|
||||
|
||||
rank1 = get_grouprank(stream1);
|
||||
rank2 = get_grouprank(stream2);
|
||||
|
||||
if (rank1 > rank2) return(1);
|
||||
else if (rank1 < rank2) return(-1);
|
||||
else
|
||||
{
|
||||
if (is_clause(stream1) && is_clause(stream2))
|
||||
return(0); /* doesn't matter what order if both are restrictions */
|
||||
else if (!is_clause(stream1) && !is_clause(stream2))
|
||||
{
|
||||
if (xfunc_num_relids(stream1) < xfunc_num_relids(stream2))
|
||||
return(-1);
|
||||
else return(1);
|
||||
}
|
||||
else if (is_clause(stream1) && !is_clause(stream2))
|
||||
{
|
||||
if (xfunc_num_relids(stream1) == xfunc_num_relids(stream2))
|
||||
/* stream1 is a restriction over stream2 */
|
||||
return(1);
|
||||
else return(-1);
|
||||
}
|
||||
else if (!is_clause(stream1) && is_clause(stream2))
|
||||
{
|
||||
/* stream2 is a restriction over stream1: never push down */
|
||||
return(-1);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* ------------------ DEBUGGING ROUTINES ---------------------------- */
|
||||
|
||||
/*
|
||||
** Make sure all pointers in stream make sense. Make sure no joins are
|
||||
** out of order.
|
||||
*/
|
||||
static bool xfunc_check_stream(Stream node)
|
||||
{
|
||||
Stream temp;
|
||||
int numrelids, tmp;
|
||||
|
||||
/* set numrelids higher than max */
|
||||
if (!is_clause(node))
|
||||
numrelids = xfunc_num_relids(node) + 1;
|
||||
else if (xfunc_get_downjoin(node))
|
||||
numrelids = xfunc_num_relids((Stream)xfunc_get_downjoin(node)) + 1;
|
||||
else numrelids = 1;
|
||||
|
||||
for (temp = node; get_downstream(temp); temp = (Stream)get_downstream(temp))
|
||||
{
|
||||
if ((Stream)get_upstream((Stream)get_downstream(temp)) != temp)
|
||||
{
|
||||
elog(WARN, "bad pointers in stream");
|
||||
return(false);
|
||||
}
|
||||
if (!is_clause(temp))
|
||||
{
|
||||
if ((tmp = xfunc_num_relids(temp)) >= numrelids)
|
||||
{
|
||||
elog(WARN, "Joins got reordered!");
|
||||
return(false);
|
||||
}
|
||||
numrelids = tmp;
|
||||
}
|
||||
}
|
||||
|
||||
return(true);
|
||||
}
|
||||
|
||||
/*
|
||||
** xfunc_in_stream
|
||||
** check if node is in stream
|
||||
*/
|
||||
static bool xfunc_in_stream(Stream node, Stream stream)
|
||||
{
|
||||
Stream temp;
|
||||
|
||||
for (temp = stream; temp; temp = (Stream)get_downstream(temp))
|
||||
if (temp == node) return(1);
|
||||
return(0);
|
||||
}
|
203
src/backend/optimizer/path/prune.c
Normal file
203
src/backend/optimizer/path/prune.c
Normal file
@@ -0,0 +1,203 @@
|
||||
/*-------------------------------------------------------------------------
|
||||
*
|
||||
* prune.c--
|
||||
* Routines to prune redundant paths and relations
|
||||
*
|
||||
* Copyright (c) 1994, Regents of the University of California
|
||||
*
|
||||
*
|
||||
* IDENTIFICATION
|
||||
* $Header: /cvsroot/pgsql/src/backend/optimizer/path/Attic/prune.c,v 1.1.1.1 1996/07/09 06:21:36 scrappy Exp $
|
||||
*
|
||||
*-------------------------------------------------------------------------
|
||||
*/
|
||||
#include "postgres.h"
|
||||
|
||||
#include "nodes/pg_list.h"
|
||||
#include "nodes/relation.h"
|
||||
|
||||
#include "optimizer/internal.h"
|
||||
#include "optimizer/cost.h"
|
||||
#include "optimizer/paths.h"
|
||||
#include "optimizer/pathnode.h"
|
||||
|
||||
#include "utils/elog.h"
|
||||
|
||||
|
||||
static List *prune_joinrel(Rel *rel, List *other_rels);
|
||||
|
||||
/*
|
||||
* prune-joinrels--
|
||||
* Removes any redundant relation entries from a list of rel nodes
|
||||
* 'rel-list'.
|
||||
*
|
||||
* Returns the resulting list.
|
||||
*
|
||||
*/
|
||||
List *prune_joinrels(List *rel_list)
|
||||
{
|
||||
List *temp_list = NIL;
|
||||
|
||||
if (rel_list != NIL) {
|
||||
temp_list = lcons(lfirst(rel_list),
|
||||
prune_joinrels(prune_joinrel((Rel*)lfirst(rel_list),
|
||||
lnext(rel_list))));
|
||||
}
|
||||
return(temp_list);
|
||||
}
|
||||
|
||||
/*
|
||||
* prune-joinrel--
|
||||
* Prunes those relations from 'other-rels' that are redundant with
|
||||
* 'rel'. A relation is redundant if it is built up of the same
|
||||
* relations as 'rel'. Paths for the redundant relation are merged into
|
||||
* the pathlist of 'rel'.
|
||||
*
|
||||
* Returns a list of non-redundant relations, and sets the pathlist field
|
||||
* of 'rel' appropriately.
|
||||
*
|
||||
*/
|
||||
static List *
|
||||
prune_joinrel(Rel *rel, List *other_rels)
|
||||
{
|
||||
List *i = NIL;
|
||||
List *t_list = NIL;
|
||||
List *temp_node = NIL;
|
||||
Rel *other_rel = (Rel *)NULL;
|
||||
|
||||
foreach(i, other_rels) {
|
||||
other_rel = (Rel*)lfirst(i);
|
||||
if(same(rel->relids, other_rel->relids)) {
|
||||
rel->pathlist = add_pathlist(rel,
|
||||
rel->pathlist,
|
||||
other_rel->pathlist);
|
||||
t_list = nconc(t_list, NIL); /* XXX is this right ? */
|
||||
} else {
|
||||
temp_node = lcons(other_rel, NIL);
|
||||
t_list = nconc(t_list,temp_node);
|
||||
}
|
||||
}
|
||||
return(t_list);
|
||||
}
|
||||
|
||||
/*
|
||||
* prune-rel-paths--
|
||||
* For each relation entry in 'rel-list' (which corresponds to a join
|
||||
* relation), set pointers to the unordered path and cheapest paths
|
||||
* (if the unordered path isn't the cheapest, it is pruned), and
|
||||
* reset the relation's size field to reflect the join.
|
||||
*
|
||||
* Returns nothing of interest.
|
||||
*
|
||||
*/
|
||||
void
|
||||
prune_rel_paths(List *rel_list)
|
||||
{
|
||||
List *x = NIL;
|
||||
List *y = NIL;
|
||||
Path *path;
|
||||
Rel *rel = (Rel*)NULL;
|
||||
JoinPath *cheapest = (JoinPath*)NULL;
|
||||
|
||||
foreach(x, rel_list) {
|
||||
rel = (Rel*)lfirst(x);
|
||||
foreach(y, rel->pathlist) {
|
||||
path = (Path*)lfirst(y);
|
||||
|
||||
if(!path->p_ordering.ord.sortop) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
cheapest = (JoinPath*)prune_rel_path(rel, path);
|
||||
if (IsA_JoinPath(cheapest))
|
||||
{
|
||||
rel->size = compute_joinrel_size(cheapest);
|
||||
}
|
||||
else
|
||||
elog(WARN, "non JoinPath called");
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* prune-rel-path--
|
||||
* Compares the unordered path for a relation with the cheapest path. If
|
||||
* the unordered path is not cheapest, it is pruned.
|
||||
*
|
||||
* Resets the pointers in 'rel' for unordered and cheapest paths.
|
||||
*
|
||||
* Returns the cheapest path.
|
||||
*
|
||||
*/
|
||||
Path *
|
||||
prune_rel_path(Rel *rel, Path *unorderedpath)
|
||||
{
|
||||
Path *cheapest = set_cheapest(rel, rel->pathlist);
|
||||
|
||||
/* don't prune if not pruneable -- JMH, 11/23/92 */
|
||||
if(unorderedpath != cheapest
|
||||
&& rel->pruneable) {
|
||||
|
||||
rel->unorderedpath = (Path *)NULL;
|
||||
rel->pathlist = lremove(unorderedpath, rel->pathlist);
|
||||
} else {
|
||||
rel->unorderedpath = (Path *)unorderedpath;
|
||||
}
|
||||
|
||||
return(cheapest);
|
||||
}
|
||||
|
||||
/*
|
||||
* merge-joinrels--
|
||||
* Given two lists of rel nodes that are already
|
||||
* pruned, merge them into one pruned rel node list
|
||||
*
|
||||
* 'rel-list1' and
|
||||
* 'rel-list2' are the rel node lists
|
||||
*
|
||||
* Returns one pruned rel node list
|
||||
*/
|
||||
List *
|
||||
merge_joinrels(List *rel_list1, List *rel_list2)
|
||||
{
|
||||
List *xrel = NIL;
|
||||
|
||||
foreach(xrel,rel_list1) {
|
||||
Rel *rel = (Rel*)lfirst(xrel);
|
||||
rel_list2 = prune_joinrel(rel,rel_list2);
|
||||
}
|
||||
return(append(rel_list1, rel_list2));
|
||||
}
|
||||
|
||||
/*
|
||||
* prune_oldrels--
|
||||
* If all the joininfo's in a rel node are inactive,
|
||||
* that means that this node has been joined into
|
||||
* other nodes in all possible ways, therefore
|
||||
* this node can be discarded. If not, it will cause
|
||||
* extra complexity of the optimizer.
|
||||
*
|
||||
* old_rels is a list of rel nodes
|
||||
*
|
||||
* Returns a new list of rel nodes
|
||||
*/
|
||||
List *prune_oldrels(List *old_rels)
|
||||
{
|
||||
Rel *rel;
|
||||
List *joininfo_list, *xjoininfo;
|
||||
|
||||
if(old_rels == NIL)
|
||||
return(NIL);
|
||||
|
||||
rel = (Rel*)lfirst(old_rels);
|
||||
joininfo_list = rel->joininfo;
|
||||
if(joininfo_list == NIL)
|
||||
return (lcons(rel, prune_oldrels(lnext(old_rels))));
|
||||
|
||||
foreach(xjoininfo, joininfo_list) {
|
||||
JInfo *joininfo = (JInfo*)lfirst(xjoininfo);
|
||||
if(!joininfo->inactive)
|
||||
return (lcons(rel, prune_oldrels(lnext(old_rels))));
|
||||
}
|
||||
return(prune_oldrels(lnext(old_rels)));
|
||||
}
|
1360
src/backend/optimizer/path/xfunc.c
Normal file
1360
src/backend/optimizer/path/xfunc.c
Normal file
File diff suppressed because it is too large
Load Diff
50
src/backend/optimizer/pathnode.h
Normal file
50
src/backend/optimizer/pathnode.h
Normal file
@@ -0,0 +1,50 @@
|
||||
/*-------------------------------------------------------------------------
|
||||
*
|
||||
* pathnode.h--
|
||||
* prototypes for pathnode.c, indexnode.c, relnode.c.
|
||||
*
|
||||
*
|
||||
* Copyright (c) 1994, Regents of the University of California
|
||||
*
|
||||
* $Id: pathnode.h,v 1.1.1.1 1996/07/09 06:21:34 scrappy Exp $
|
||||
*
|
||||
*-------------------------------------------------------------------------
|
||||
*/
|
||||
#ifndef PATHNODE_H
|
||||
#define PATHNODE_H
|
||||
|
||||
/*
|
||||
* prototypes for pathnode.c
|
||||
*/
|
||||
extern bool path_is_cheaper(Path *path1, Path *path2);
|
||||
extern Path *set_cheapest(Rel *parent_rel, List *pathlist);
|
||||
extern List *add_pathlist(Rel *parent_rel, List *unique_paths,
|
||||
List *new_paths);
|
||||
extern Path *create_seqscan_path(Rel *rel);
|
||||
extern IndexPath *create_index_path(Query *root, Rel *rel, Rel *index,
|
||||
List *restriction_clauses, bool is_join_scan);
|
||||
extern JoinPath *create_nestloop_path(Rel *joinrel, Rel *outer_rel,
|
||||
Path *outer_path, Path *inner_path, List *keys);
|
||||
extern MergePath *create_mergesort_path(Rel *joinrel, int outersize,
|
||||
int innersize, int outerwidth, int innerwidth, Path *outer_path,
|
||||
Path *inner_path, List *keys, MergeOrder *order,
|
||||
List *mergeclauses, List *outersortkeys, List *innersortkeys);
|
||||
|
||||
extern HashPath *create_hashjoin_path(Rel *joinrel, int outersize,
|
||||
int innersize, int outerwidth, int innerwidth, Path *outer_path,
|
||||
Path *inner_path, List *keys, Oid operator, List *hashclauses,
|
||||
List *outerkeys, List *innerkeys);
|
||||
|
||||
/*
|
||||
* prototypes for rel.c
|
||||
*/
|
||||
extern Rel *rel_member(List *relid, List *rels);
|
||||
extern Rel *get_base_rel(Query* root, int relid);
|
||||
extern Rel *get_join_rel(Query* root, List *relid);
|
||||
|
||||
/*
|
||||
* prototypes for indexnode.h
|
||||
*/
|
||||
extern List *find_relation_indices(Query *root,Rel *rel);
|
||||
|
||||
#endif /* PATHNODE_H */
|
89
src/backend/optimizer/paths.h
Normal file
89
src/backend/optimizer/paths.h
Normal file
@@ -0,0 +1,89 @@
|
||||
/*-------------------------------------------------------------------------
|
||||
*
|
||||
* paths.h--
|
||||
* prototypes for various files in optimizer/paths (were separate
|
||||
* header files
|
||||
*
|
||||
*
|
||||
* Copyright (c) 1994, Regents of the University of California
|
||||
*
|
||||
* $Id: paths.h,v 1.1.1.1 1996/07/09 06:21:34 scrappy Exp $
|
||||
*
|
||||
*-------------------------------------------------------------------------
|
||||
*/
|
||||
#ifndef PATHS_H
|
||||
#define PATHS_H
|
||||
|
||||
/*
|
||||
* allpaths.h
|
||||
*/
|
||||
extern List *find_paths(Query *root, List *rels);
|
||||
|
||||
/*
|
||||
* indxpath.h
|
||||
* routines to generate index paths
|
||||
*/
|
||||
extern List *find_index_paths(Query *root, Rel *rel, List *indices,
|
||||
List *clauseinfo_list,
|
||||
List *joininfo_list);
|
||||
|
||||
/*
|
||||
* joinpath.h
|
||||
* routines to create join paths
|
||||
*/
|
||||
extern void find_all_join_paths(Query *root, List *joinrels);
|
||||
|
||||
|
||||
/*
|
||||
* orindxpath.h
|
||||
*/
|
||||
extern List *create_or_index_paths(Query *root, Rel *rel, List *clauses);
|
||||
|
||||
/*
|
||||
* hashutils.h
|
||||
* routines to deal with hash keys and clauses
|
||||
*/
|
||||
extern List *group_clauses_by_hashop(List *clauseinfo_list,
|
||||
int inner_relid);
|
||||
|
||||
/*
|
||||
* joinutils.h
|
||||
* generic join method key/clause routines
|
||||
*/
|
||||
extern List *match_pathkeys_joinkeys(List *pathkeys,
|
||||
List *joinkeys, List *joinclauses, int which_subkey,
|
||||
List **matchedJoinClausesPtr);
|
||||
extern List *extract_path_keys(List *joinkeys, List *tlist,
|
||||
int which_subkey);
|
||||
extern Path *match_paths_joinkeys(List *joinkeys, PathOrder *ordering,
|
||||
List *paths, int which_subkey);
|
||||
extern List *new_join_pathkeys(List *outer_pathkeys,
|
||||
List *join_rel_tlist, List *joinclauses);
|
||||
|
||||
/*
|
||||
* mergeutils.h
|
||||
* routines to deal with merge keys and clauses
|
||||
*/
|
||||
extern List *group_clauses_by_order(List *clauseinfo_list,
|
||||
int inner_relid);
|
||||
extern MInfo *match_order_mergeinfo(PathOrder *ordering,
|
||||
List *mergeinfo_list);
|
||||
|
||||
/*
|
||||
* joinrels.h
|
||||
* routines to determine which relations to join
|
||||
*/
|
||||
extern List *find_join_rels(Query *root, List *outer_rels);
|
||||
extern void add_new_joininfos(Query *root, List *joinrels, List *outerrels);
|
||||
extern List *final_join_rels(List *join_rel_list);
|
||||
|
||||
/*
|
||||
* prototypes for path/prune.c
|
||||
*/
|
||||
extern List *prune_joinrels(List *rel_list);
|
||||
extern void prune_rel_paths(List *rel_list);
|
||||
extern Path *prune_rel_path(Rel *rel, Path *unorderedpath);
|
||||
extern List *merge_joinrels(List *rel_list1, List *rel_list2);
|
||||
extern List *prune_oldrels(List *old_rels);
|
||||
|
||||
#endif /* PATHS_H */
|
15
src/backend/optimizer/plan/Makefile.inc
Normal file
15
src/backend/optimizer/plan/Makefile.inc
Normal file
@@ -0,0 +1,15 @@
|
||||
#-------------------------------------------------------------------------
|
||||
#
|
||||
# Makefile.inc--
|
||||
# Makefile for optimizer/plan
|
||||
#
|
||||
# Copyright (c) 1994, Regents of the University of California
|
||||
#
|
||||
#
|
||||
# IDENTIFICATION
|
||||
# $Header: /cvsroot/pgsql/src/backend/optimizer/plan/Attic/Makefile.inc,v 1.1.1.1 1996/07/09 06:21:37 scrappy Exp $
|
||||
#
|
||||
#-------------------------------------------------------------------------
|
||||
|
||||
SUBSRCS+= createplan.c initsplan.c planmain.c planner.c \
|
||||
setrefs.c
|
1097
src/backend/optimizer/plan/createplan.c
Normal file
1097
src/backend/optimizer/plan/createplan.c
Normal file
File diff suppressed because it is too large
Load Diff
391
src/backend/optimizer/plan/initsplan.c
Normal file
391
src/backend/optimizer/plan/initsplan.c
Normal file
@@ -0,0 +1,391 @@
|
||||
/*-------------------------------------------------------------------------
|
||||
*
|
||||
* initsplan.c--
|
||||
* Target list, qualification, joininfo initialization routines
|
||||
*
|
||||
* Copyright (c) 1994, Regents of the University of California
|
||||
*
|
||||
*
|
||||
* IDENTIFICATION
|
||||
* $Header: /cvsroot/pgsql/src/backend/optimizer/plan/initsplan.c,v 1.1.1.1 1996/07/09 06:21:37 scrappy Exp $
|
||||
*
|
||||
*-------------------------------------------------------------------------
|
||||
*/
|
||||
#include "postgres.h"
|
||||
|
||||
#include "nodes/pg_list.h"
|
||||
#include "nodes/plannodes.h"
|
||||
#include "nodes/parsenodes.h"
|
||||
#include "nodes/relation.h"
|
||||
#include "nodes/makefuncs.h"
|
||||
|
||||
#include "utils/lsyscache.h"
|
||||
#include "utils/palloc.h"
|
||||
|
||||
#include "optimizer/internal.h"
|
||||
#include "optimizer/planmain.h"
|
||||
#include "optimizer/joininfo.h"
|
||||
#include "optimizer/pathnode.h"
|
||||
#include "optimizer/tlist.h"
|
||||
#include "optimizer/var.h"
|
||||
#include "optimizer/clauses.h"
|
||||
#include "optimizer/cost.h"
|
||||
|
||||
extern int Quiet;
|
||||
|
||||
static void add_clause_to_rels(Query *root, List *clause);
|
||||
static void add_join_clause_info_to_rels(Query *root, CInfo *clauseinfo,
|
||||
List *join_relids);
|
||||
static void add_vars_to_rels(Query *root, List *vars, List *join_relids);
|
||||
|
||||
static MergeOrder *mergesortop(Expr *clause);
|
||||
static Oid hashjoinop(Expr *clause);
|
||||
|
||||
|
||||
/*****************************************************************************
|
||||
*
|
||||
* TARGET LISTS
|
||||
*
|
||||
*****************************************************************************/
|
||||
|
||||
/*
|
||||
* initialize_rel_nodes--
|
||||
* Creates rel nodes for every relation mentioned in the target list
|
||||
* 'tlist' (if a node hasn't already been created) and adds them to
|
||||
* *query-relation-list*. Creates targetlist entries for each member of
|
||||
* 'tlist' and adds them to the tlist field of the appropriate rel node.
|
||||
*
|
||||
* Returns nothing.
|
||||
*/
|
||||
void
|
||||
initialize_base_rels_list(Query *root, List *tlist)
|
||||
{
|
||||
List *tlist_vars = NIL;
|
||||
List *l = NIL;
|
||||
List *tvar = NIL;
|
||||
|
||||
foreach (l, tlist) {
|
||||
TargetEntry *entry = (TargetEntry *) lfirst(l);
|
||||
|
||||
tlist_vars = append(tlist_vars, pull_var_clause(entry->expr));
|
||||
}
|
||||
|
||||
/* now, the target list only contains Var nodes */
|
||||
foreach (tvar, tlist_vars) {
|
||||
Var *var;
|
||||
Index varno;
|
||||
Rel *result;
|
||||
|
||||
var = (Var*)lfirst(tvar);
|
||||
varno = var->varno;
|
||||
result = get_base_rel(root, varno);
|
||||
|
||||
add_tl_element(result, var);
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* add_missing_variables_to_base_rels -
|
||||
* If we have range variable(s) in the FROM clause that does not appear
|
||||
* in the target list nor qualifications, we add it to the base relation
|
||||
* list. For instance, "select f.x from foo f, foo f2" is a join of f and
|
||||
* f2. Note that if we have "select foo.x from foo f", it also gets turned
|
||||
* into a join.
|
||||
*/
|
||||
void
|
||||
add_missing_vars_to_base_rels(Query *root, List *tlist)
|
||||
{
|
||||
List *l;
|
||||
int varno;
|
||||
|
||||
varno = 1;
|
||||
foreach (l, root->rtable) {
|
||||
RangeTblEntry *rte = (RangeTblEntry *)lfirst(l);
|
||||
List *relids;
|
||||
Rel *result;
|
||||
Var *var;
|
||||
|
||||
relids = lconsi(varno, NIL);
|
||||
if (rte->inFromCl &&
|
||||
!rel_member(relids, root->base_relation_list_)) {
|
||||
|
||||
var = makeVar(varno, -2 , 26, varno, -2);
|
||||
/* add it to base_relation_list_ */
|
||||
result = get_base_rel(root, varno);
|
||||
add_tl_element(result, var);
|
||||
}
|
||||
pfree(relids);
|
||||
varno++;
|
||||
}
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
/*****************************************************************************
|
||||
*
|
||||
* QUALIFICATIONS
|
||||
*
|
||||
*****************************************************************************/
|
||||
|
||||
|
||||
|
||||
/*
|
||||
* initialize-qualification--
|
||||
* Initializes ClauseInfo and JoinInfo fields of relation entries for all
|
||||
* relations appearing within clauses. Creates new relation entries if
|
||||
* necessary, adding them to *query-relation-list*.
|
||||
*
|
||||
* Returns nothing of interest.
|
||||
*/
|
||||
void
|
||||
initialize_base_rels_jinfo(Query *root, List *clauses)
|
||||
{
|
||||
List *clause;
|
||||
|
||||
foreach (clause, clauses) {
|
||||
add_clause_to_rels(root, lfirst(clause));
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
/*
|
||||
* add-clause-to-rels--
|
||||
* Add clause information to either the 'ClauseInfo' or 'JoinInfo' field
|
||||
* of a relation entry(depending on whether or not the clause is a join)
|
||||
* by creating a new ClauseInfo node and setting appropriate fields
|
||||
* within the nodes.
|
||||
*
|
||||
* Returns nothing of interest.
|
||||
*/
|
||||
static void
|
||||
add_clause_to_rels(Query *root, List *clause)
|
||||
{
|
||||
List *relids;
|
||||
List *vars;
|
||||
CInfo *clauseinfo = makeNode(CInfo);
|
||||
|
||||
/*
|
||||
* Retrieve all relids and vars contained within the clause.
|
||||
*/
|
||||
clause_relids_vars((Node*)clause, &relids, &vars);
|
||||
|
||||
|
||||
clauseinfo->clause = (Expr*)clause;
|
||||
clauseinfo->notclause = contains_not((Node*)clause);
|
||||
clauseinfo->selectivity = 0;
|
||||
clauseinfo->indexids = NIL;
|
||||
clauseinfo->mergesortorder = (MergeOrder*)NULL;
|
||||
clauseinfo->hashjoinoperator = (Oid)0;
|
||||
|
||||
|
||||
|
||||
if(length(relids) == 1) {
|
||||
Rel *rel = get_base_rel(root, lfirsti(relids));
|
||||
|
||||
/*
|
||||
* There is only one relation participating in 'clause',
|
||||
* so 'clause' must be a restriction clause.
|
||||
*/
|
||||
|
||||
/* the selectivity of the clause must be computed
|
||||
regardless of whether it's a restriction or a join clause */
|
||||
if (is_funcclause((Node*)clause))
|
||||
{
|
||||
/*
|
||||
* XXX If we have a func clause set selectivity to 1/3,
|
||||
* really need a true selectivity function.
|
||||
*/
|
||||
clauseinfo->selectivity = (Cost)0.3333333;
|
||||
}
|
||||
else
|
||||
{
|
||||
clauseinfo->selectivity =
|
||||
compute_clause_selec(root, (Node*)clause,
|
||||
NIL);
|
||||
}
|
||||
rel->clauseinfo = lcons(clauseinfo,
|
||||
rel->clauseinfo);
|
||||
} else {
|
||||
/*
|
||||
* 'clause' is a join clause, since there is more than one
|
||||
* atom in the relid list.
|
||||
*/
|
||||
|
||||
if (is_funcclause((Node*)clause))
|
||||
{
|
||||
/*
|
||||
* XXX If we have a func clause set selectivity to 1/3,
|
||||
* really need a true selectivity function.
|
||||
*/
|
||||
clauseinfo->selectivity = (Cost)0.3333333;
|
||||
}
|
||||
else
|
||||
{
|
||||
clauseinfo->selectivity =
|
||||
compute_clause_selec(root, (Node*)clause,
|
||||
NIL);
|
||||
}
|
||||
add_join_clause_info_to_rels(root, clauseinfo, relids);
|
||||
add_vars_to_rels(root,vars, relids);
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* add-join-clause-info-to-rels--
|
||||
* For every relation participating in a join clause, add 'clauseinfo' to
|
||||
* the appropriate joininfo node(creating a new one and adding it to the
|
||||
* appropriate rel node if necessary).
|
||||
*
|
||||
* 'clauseinfo' describes the join clause
|
||||
* 'join-relids' is the list of relations participating in the join clause
|
||||
*
|
||||
* Returns nothing.
|
||||
*
|
||||
*/
|
||||
static void
|
||||
add_join_clause_info_to_rels(Query *root, CInfo *clauseinfo, List *join_relids)
|
||||
{
|
||||
List *join_relid;
|
||||
|
||||
foreach (join_relid, join_relids) {
|
||||
JInfo *joininfo =
|
||||
find_joininfo_node(get_base_rel(root, lfirsti(join_relid)),
|
||||
intLispRemove((int)lfirst(join_relid),
|
||||
join_relids));
|
||||
joininfo->jinfoclauseinfo =
|
||||
lcons(clauseinfo, joininfo->jinfoclauseinfo);
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* add-vars-to-rels--
|
||||
* For each variable appearing in a clause,
|
||||
* (1) If a targetlist entry for the variable is not already present in
|
||||
* the appropriate relation's target list, add one.
|
||||
* (2) If a targetlist entry is already present, but the var is part of a
|
||||
* join clause, add the relids of the join relations to the JoinList
|
||||
* entry of the targetlist entry.
|
||||
*
|
||||
* 'vars' is the list of var nodes
|
||||
* 'join-relids' is the list of relids appearing in the join clause
|
||||
* (if this is a join clause)
|
||||
*
|
||||
* Returns nothing.
|
||||
*/
|
||||
static void
|
||||
add_vars_to_rels(Query *root, List *vars, List *join_relids)
|
||||
{
|
||||
Var *var;
|
||||
List *temp = NIL;
|
||||
Rel *rel = (Rel*)NULL;
|
||||
TargetEntry *tlistentry;
|
||||
|
||||
foreach (temp, vars) {
|
||||
var = (Var*)lfirst(temp);
|
||||
rel = get_base_rel(root, var->varno);
|
||||
tlistentry = tlistentry_member(var, rel->targetlist);
|
||||
if(tlistentry==NULL)
|
||||
/* add a new entry */
|
||||
add_tl_element(rel, var);
|
||||
}
|
||||
}
|
||||
|
||||
/*****************************************************************************
|
||||
*
|
||||
* JOININFO
|
||||
*
|
||||
*****************************************************************************/
|
||||
|
||||
/*
|
||||
* initialize-join-clause-info--
|
||||
* Set the MergeSortable or HashJoinable field for every joininfo node
|
||||
* (within a rel node) and the MergeSortOrder or HashJoinOp field for
|
||||
* each clauseinfo node(within a joininfo node) for all relations in a
|
||||
* query.
|
||||
*
|
||||
* Returns nothing.
|
||||
*/
|
||||
void
|
||||
initialize_join_clause_info(List *rel_list)
|
||||
{
|
||||
List *x, *y, *z;
|
||||
Rel *rel;
|
||||
JInfo *joininfo;
|
||||
CInfo *clauseinfo;
|
||||
Expr *clause;
|
||||
|
||||
foreach (x, rel_list) {
|
||||
rel = (Rel*)lfirst(x);
|
||||
foreach (y, rel->joininfo) {
|
||||
joininfo = (JInfo*)lfirst(y);
|
||||
foreach (z, joininfo->jinfoclauseinfo) {
|
||||
clauseinfo = (CInfo*)lfirst(z);
|
||||
clause = clauseinfo->clause;
|
||||
if(join_clause_p((Node*)clause)) {
|
||||
MergeOrder *sortop = (MergeOrder*)NULL;
|
||||
Oid hashop = (Oid)NULL;
|
||||
|
||||
if (_enable_mergesort_)
|
||||
sortop = mergesortop(clause);
|
||||
if (_enable_hashjoin_)
|
||||
hashop = hashjoinop(clause);
|
||||
|
||||
if (sortop) {
|
||||
clauseinfo->mergesortorder = sortop;
|
||||
joininfo->mergesortable = true;
|
||||
}
|
||||
if (hashop) {
|
||||
clauseinfo->hashjoinoperator = hashop;
|
||||
joininfo->hashjoinable = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* mergesortop--
|
||||
* Returns the mergesort operator of an operator iff 'clause' is
|
||||
* mergesortable, i.e., both operands are single vars and the operator is
|
||||
* a mergesortable operator.
|
||||
*/
|
||||
static MergeOrder *
|
||||
mergesortop(Expr *clause)
|
||||
{
|
||||
Oid leftOp, rightOp;
|
||||
bool sortable;
|
||||
|
||||
sortable = op_mergesortable(((Oper*)clause->oper)->opno,
|
||||
(get_leftop(clause))->vartype,
|
||||
(get_rightop(clause))->vartype,
|
||||
&leftOp,
|
||||
&rightOp);
|
||||
|
||||
if (sortable) {
|
||||
MergeOrder *morder = makeNode(MergeOrder);
|
||||
|
||||
morder->join_operator = ((Oper*)clause->oper)->opno;
|
||||
morder->left_operator = leftOp;
|
||||
morder->right_operator = rightOp;
|
||||
morder->left_type = (get_leftop(clause))->vartype;
|
||||
morder->right_type = (get_rightop(clause))->vartype;
|
||||
return (morder);
|
||||
} else
|
||||
return(NULL);
|
||||
}
|
||||
|
||||
/*
|
||||
* hashjoinop--
|
||||
* Returns the hashjoin operator of an operator iff 'clause' is
|
||||
* hashjoinable, i.e., both operands are single vars and the operator is
|
||||
* a hashjoinable operator.
|
||||
*/
|
||||
static Oid
|
||||
hashjoinop(Expr *clause)
|
||||
{
|
||||
return(op_hashjoinable(((Oper*)clause->oper)->opno,
|
||||
(get_leftop(clause))->vartype,
|
||||
(get_rightop(clause))->vartype));
|
||||
}
|
422
src/backend/optimizer/plan/planmain.c
Normal file
422
src/backend/optimizer/plan/planmain.c
Normal file
@@ -0,0 +1,422 @@
|
||||
/*-------------------------------------------------------------------------
|
||||
*
|
||||
* planmain.c--
|
||||
* Routines to plan a single query
|
||||
*
|
||||
* Copyright (c) 1994, Regents of the University of California
|
||||
*
|
||||
*
|
||||
* IDENTIFICATION
|
||||
* $Header: /cvsroot/pgsql/src/backend/optimizer/plan/planmain.c,v 1.1.1.1 1996/07/09 06:21:37 scrappy Exp $
|
||||
*
|
||||
*-------------------------------------------------------------------------
|
||||
*/
|
||||
#include "postgres.h"
|
||||
|
||||
#include "nodes/pg_list.h"
|
||||
#include "nodes/plannodes.h"
|
||||
#include "nodes/parsenodes.h"
|
||||
#include "nodes/relation.h"
|
||||
|
||||
#include "optimizer/planmain.h"
|
||||
#include "optimizer/internal.h"
|
||||
#include "optimizer/paths.h"
|
||||
#include "optimizer/clauses.h"
|
||||
#include "optimizer/keys.h"
|
||||
#include "optimizer/tlist.h"
|
||||
#include "optimizer/xfunc.h"
|
||||
#include "optimizer/cost.h"
|
||||
|
||||
#include "tcop/dest.h"
|
||||
#include "utils/elog.h"
|
||||
#include "utils/palloc.h"
|
||||
#include "nodes/memnodes.h"
|
||||
#include "utils/mcxt.h"
|
||||
#include "utils/lsyscache.h"
|
||||
|
||||
static Plan *subplanner(Query *root, List *flat_tlist, List *qual);
|
||||
static Result *make_result(List *tlist, Node *resconstantqual, Plan *subplan);
|
||||
|
||||
static Plan *make_groupPlan(List *tlist, bool tuplePerGroup,
|
||||
List *groupClause, Plan *subplan);
|
||||
|
||||
/*
|
||||
* query_planner--
|
||||
* Routine to create a query plan. It does so by first creating a
|
||||
* subplan for the topmost level of attributes in the query. Then,
|
||||
* it modifies all target list and qualifications to consider the next
|
||||
* level of nesting and creates a plan for this modified query by
|
||||
* recursively calling itself. The two pieces are then merged together
|
||||
* by creating a result node that indicates which attributes should
|
||||
* be placed where and any relation level qualifications to be
|
||||
* satisfied.
|
||||
*
|
||||
* command-type is the query command, e.g., retrieve, delete, etc.
|
||||
* tlist is the target list of the query
|
||||
* qual is the qualification of the query
|
||||
*
|
||||
* Returns a query plan.
|
||||
*/
|
||||
Plan *
|
||||
query_planner(Query *root,
|
||||
int command_type,
|
||||
List *tlist,
|
||||
List *qual)
|
||||
{
|
||||
List *constant_qual = NIL;
|
||||
List *flattened_tlist = NIL;
|
||||
List *level_tlist = NIL;
|
||||
Plan *subplan = (Plan*)NULL;
|
||||
Agg *aggplan = NULL;
|
||||
|
||||
/*
|
||||
* A command without a target list or qualification is an error,
|
||||
* except for "delete foo".
|
||||
*/
|
||||
if (tlist==NIL && qual==NULL) {
|
||||
if (command_type == CMD_DELETE ||
|
||||
/* Total hack here. I don't know how to handle
|
||||
statements like notify in action bodies.
|
||||
Notify doesn't return anything but
|
||||
scans a system table. */
|
||||
command_type == CMD_NOTIFY) {
|
||||
return ((Plan*)make_seqscan(NIL,
|
||||
NIL,
|
||||
root->resultRelation,
|
||||
(Plan*)NULL));
|
||||
} else
|
||||
return((Plan*)NULL);
|
||||
}
|
||||
|
||||
/*
|
||||
* Pull out any non-variable qualifications so these can be put in
|
||||
* the topmost result node. The opids for the remaining
|
||||
* qualifications will be changed to regprocs later.
|
||||
*/
|
||||
qual = pull_constant_clauses(qual, &constant_qual);
|
||||
fix_opids(constant_qual);
|
||||
|
||||
/*
|
||||
* Create a target list that consists solely of (resdom var) target
|
||||
* list entries, i.e., contains no arbitrary expressions.
|
||||
*/
|
||||
flattened_tlist = flatten_tlist(tlist);
|
||||
if (flattened_tlist) {
|
||||
level_tlist = flattened_tlist;
|
||||
} else {
|
||||
/* from old code. the logic is beyond me. - ay 2/95 */
|
||||
level_tlist = tlist;
|
||||
}
|
||||
|
||||
/*
|
||||
* Needs to add the group attribute(s) to the target list so that they
|
||||
* are available to either the Group node or the Agg node. (The target
|
||||
* list may not contain the group attribute(s).)
|
||||
*/
|
||||
if (root->groupClause) {
|
||||
AddGroupAttrToTlist(level_tlist, root->groupClause);
|
||||
}
|
||||
|
||||
if (root->qry_aggs) {
|
||||
aggplan = make_agg(tlist, root->qry_numAgg, root->qry_aggs);
|
||||
tlist = level_tlist;
|
||||
}
|
||||
|
||||
/*
|
||||
* A query may have a non-variable target list and a non-variable
|
||||
* qualification only under certain conditions:
|
||||
* - the query creates all-new tuples, or
|
||||
* - the query is a replace (a scan must still be done in this case).
|
||||
*/
|
||||
if (flattened_tlist==NULL && qual==NULL) {
|
||||
|
||||
switch (command_type) {
|
||||
case CMD_SELECT:
|
||||
case CMD_INSERT:
|
||||
return ((Plan*)make_result(tlist,
|
||||
(Node*)constant_qual,
|
||||
(Plan*)NULL));
|
||||
break;
|
||||
|
||||
case CMD_DELETE:
|
||||
case CMD_UPDATE:
|
||||
{
|
||||
SeqScan *scan = make_seqscan(tlist,
|
||||
(List *)NULL,
|
||||
root->resultRelation,
|
||||
(Plan*)NULL);
|
||||
if (constant_qual!=NULL) {
|
||||
return ((Plan*)make_result(tlist,
|
||||
(Node*)constant_qual,
|
||||
(Plan*)scan));
|
||||
} else {
|
||||
return ((Plan*)scan);
|
||||
}
|
||||
}
|
||||
break;
|
||||
|
||||
default:
|
||||
return ((Plan*)NULL);
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* Find the subplan (access path) and destructively modify the
|
||||
* target list of the newly created subplan to contain the appropriate
|
||||
* join references.
|
||||
*/
|
||||
subplan = subplanner(root, level_tlist, qual);
|
||||
|
||||
set_tlist_references(subplan);
|
||||
|
||||
/*
|
||||
* If we have a GROUP BY clause, insert a group node (with the appropriate
|
||||
* sort node.)
|
||||
*/
|
||||
if (root->groupClause != NULL) {
|
||||
bool tuplePerGroup;
|
||||
|
||||
/*
|
||||
* decide whether how many tuples per group the Group node needs
|
||||
* to return. (Needs only one tuple per group if no aggregate is
|
||||
* present. Otherwise, need every tuple from the group to do the
|
||||
* aggregation.)
|
||||
*/
|
||||
tuplePerGroup = (aggplan == NULL) ? FALSE : TRUE;
|
||||
|
||||
subplan =
|
||||
make_groupPlan(tlist, tuplePerGroup, root->groupClause, subplan);
|
||||
|
||||
/* XXX fake it: this works for the Group node too! very very ugly,
|
||||
please change me -ay 2/95 */
|
||||
set_agg_tlist_references((Agg*)subplan);
|
||||
}
|
||||
|
||||
/*
|
||||
* If aggregate is present, insert the agg node
|
||||
*/
|
||||
if (aggplan != NULL) {
|
||||
aggplan->plan.lefttree = subplan;
|
||||
subplan = (Plan*)aggplan;
|
||||
|
||||
/*
|
||||
* set the varno/attno entries to the appropriate references to
|
||||
* the result tuple of the subplans. (We need to set those in the
|
||||
* array of aggreg's in the Agg node also. Even though they're
|
||||
* pointers, after a few dozen's of copying, they're not the same as
|
||||
* those in the target list.)
|
||||
*/
|
||||
set_agg_tlist_references((Agg*)subplan);
|
||||
set_agg_agglist_references((Agg*)subplan);
|
||||
|
||||
tlist = aggplan->plan.targetlist;
|
||||
}
|
||||
|
||||
/*
|
||||
* Build a result node linking the plan if we have constant quals
|
||||
*/
|
||||
if (constant_qual) {
|
||||
Plan *plan;
|
||||
|
||||
plan = (Plan*)make_result(tlist,
|
||||
(Node*)constant_qual,
|
||||
subplan);
|
||||
/*
|
||||
* Change all varno's of the Result's node target list.
|
||||
*/
|
||||
set_result_tlist_references((Result*)plan);
|
||||
|
||||
return (plan);
|
||||
}
|
||||
|
||||
/*
|
||||
* fix up the flattened target list of the plan root node so that
|
||||
* expressions are evaluated. this forces expression evaluations
|
||||
* that may involve expensive function calls to be delayed to
|
||||
* the very last stage of query execution. this could be bad.
|
||||
* but it is joey's responsibility to optimally push these
|
||||
* expressions down the plan tree. -- Wei
|
||||
*/
|
||||
subplan->targetlist = flatten_tlist_vars(tlist,
|
||||
subplan->targetlist);
|
||||
|
||||
/*
|
||||
* Destructively modify the query plan's targetlist to add fjoin
|
||||
* lists to flatten functions that return sets of base types
|
||||
*/
|
||||
subplan->targetlist = generate_fjoin(subplan->targetlist);
|
||||
|
||||
return (subplan);
|
||||
}
|
||||
|
||||
/*
|
||||
* subplanner
|
||||
*
|
||||
* Subplanner creates an entire plan consisting of joins and scans
|
||||
* for processing a single level of attributes.
|
||||
*
|
||||
* flat-tlist is the flattened target list
|
||||
* qual is the qualification to be satisfied
|
||||
*
|
||||
* Returns a subplan.
|
||||
*
|
||||
*/
|
||||
static Plan *
|
||||
subplanner(Query *root,
|
||||
List *flat_tlist,
|
||||
List *qual)
|
||||
{
|
||||
Rel *final_relation;
|
||||
List *final_relation_list;
|
||||
|
||||
/* Initialize the targetlist and qualification, adding entries to
|
||||
* *query-relation-list* as relation references are found (e.g., in the
|
||||
* qualification, the targetlist, etc.)
|
||||
*/
|
||||
root->base_relation_list_ = NIL;
|
||||
root->join_relation_list_ = NIL;
|
||||
initialize_base_rels_list(root, flat_tlist);
|
||||
initialize_base_rels_jinfo(root, qual);
|
||||
add_missing_vars_to_base_rels(root, flat_tlist);
|
||||
|
||||
/* Find all possible scan and join paths.
|
||||
* Mark all the clauses and relations that can be processed using special
|
||||
* join methods, then do the exhaustive path search.
|
||||
*/
|
||||
initialize_join_clause_info(root->base_relation_list_);
|
||||
final_relation_list = find_paths(root,
|
||||
root->base_relation_list_);
|
||||
|
||||
if (final_relation_list)
|
||||
final_relation = (Rel*)lfirst (final_relation_list);
|
||||
else
|
||||
final_relation = (Rel*)NIL;
|
||||
|
||||
#if 0 /* fix xfunc */
|
||||
/*
|
||||
* Perform Predicate Migration on each path, to optimize and correctly
|
||||
* assess the cost of each before choosing the cheapest one.
|
||||
* -- JMH, 11/16/92
|
||||
*
|
||||
* Needn't do so if the top rel is pruneable: that means there's no
|
||||
* expensive functions left to pull up. -- JMH, 11/22/92
|
||||
*/
|
||||
if (XfuncMode != XFUNC_OFF && XfuncMode != XFUNC_NOPM &&
|
||||
XfuncMode != XFUNC_NOPULL && !final_relation->pruneable)
|
||||
{
|
||||
List *pathnode;
|
||||
foreach(pathnode, final_relation->pathlist)
|
||||
{
|
||||
if (xfunc_do_predmig((Path*)lfirst(pathnode)))
|
||||
set_cheapest(final_relation, final_relation->pathlist);
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
/*
|
||||
* Determine the cheapest path and create a subplan corresponding to it.
|
||||
*/
|
||||
if (final_relation) {
|
||||
return (create_plan ((Path*)final_relation->cheapestpath));
|
||||
}else {
|
||||
elog(NOTICE, "final relation is nil");
|
||||
return(create_plan ((Path*)NULL));
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/*****************************************************************************
|
||||
*
|
||||
*****************************************************************************/
|
||||
|
||||
static Result *
|
||||
make_result(List *tlist,
|
||||
Node *resconstantqual,
|
||||
Plan *subplan)
|
||||
{
|
||||
Result *node = makeNode(Result);
|
||||
Plan *plan = &node->plan;
|
||||
|
||||
tlist = generate_fjoin(tlist);
|
||||
plan->cost = 0.0;
|
||||
plan->state = (EState *)NULL;
|
||||
plan->targetlist = tlist;
|
||||
plan->lefttree = subplan;
|
||||
plan->righttree = NULL;
|
||||
node->resconstantqual = resconstantqual;
|
||||
node->resstate = NULL;
|
||||
|
||||
return(node);
|
||||
}
|
||||
|
||||
/*****************************************************************************
|
||||
*
|
||||
*****************************************************************************/
|
||||
|
||||
static Plan *
|
||||
make_groupPlan(List *tlist,
|
||||
bool tuplePerGroup,
|
||||
List *groupClause,
|
||||
Plan *subplan)
|
||||
{
|
||||
List *sort_tlist;
|
||||
List *gl;
|
||||
int keyno;
|
||||
Sort *sortplan;
|
||||
Group *grpplan;
|
||||
int numCols;
|
||||
AttrNumber *grpColIdx;
|
||||
|
||||
numCols = length(groupClause);
|
||||
grpColIdx = (AttrNumber *)palloc(sizeof(AttrNumber)*numCols);
|
||||
|
||||
/*
|
||||
* first, make a sort node. Group node expects the tuples it gets
|
||||
* from the subplan is in the order as specified by the group columns.
|
||||
*/
|
||||
keyno = 1;
|
||||
sort_tlist = new_unsorted_tlist(subplan->targetlist);
|
||||
|
||||
{
|
||||
/* if this is a mergejoin node, varno could be OUTER/INNER */
|
||||
List *l;
|
||||
foreach(l, sort_tlist) {
|
||||
TargetEntry *tle;
|
||||
tle = lfirst(l);
|
||||
((Var*)tle->expr)->varno = 1;
|
||||
}
|
||||
}
|
||||
|
||||
foreach (gl, groupClause) {
|
||||
GroupClause *grpcl = (GroupClause*)lfirst(gl);
|
||||
TargetEntry *tle;
|
||||
|
||||
tle = match_varid(grpcl->grpAttr, sort_tlist);
|
||||
/*
|
||||
* the parser should have checked to make sure the group attribute
|
||||
* is valid but the optimizer might have screwed up and hence we
|
||||
* check again.
|
||||
*/
|
||||
if (tle==NULL) {
|
||||
elog(WARN, "group attribute disappeared from target list");
|
||||
}
|
||||
tle->resdom->reskey = keyno;
|
||||
tle->resdom->reskeyop = get_opcode(grpcl->grpOpoid);
|
||||
|
||||
grpColIdx[keyno-1] = tle->resdom->resno;
|
||||
keyno++;
|
||||
}
|
||||
sortplan = make_sort(sort_tlist,
|
||||
_TEMP_RELATION_ID_,
|
||||
subplan,
|
||||
numCols);
|
||||
sortplan->plan.cost = subplan->cost; /* XXX assume no cost */
|
||||
|
||||
/*
|
||||
* make the Group node
|
||||
*/
|
||||
tlist = copyObject(tlist); /* make a copy */
|
||||
grpplan = make_group(tlist, tuplePerGroup, numCols, grpColIdx, sortplan);
|
||||
|
||||
return (Plan*)grpplan;
|
||||
}
|
408
src/backend/optimizer/plan/planner.c
Normal file
408
src/backend/optimizer/plan/planner.c
Normal file
@@ -0,0 +1,408 @@
|
||||
/*-------------------------------------------------------------------------
|
||||
*
|
||||
* planner.c--
|
||||
* The query optimizer external interface.
|
||||
*
|
||||
* Copyright (c) 1994, Regents of the University of California
|
||||
*
|
||||
*
|
||||
* IDENTIFICATION
|
||||
* $Header: /cvsroot/pgsql/src/backend/optimizer/plan/planner.c,v 1.1.1.1 1996/07/09 06:21:37 scrappy Exp $
|
||||
*
|
||||
*-------------------------------------------------------------------------
|
||||
*/
|
||||
#include "postgres.h"
|
||||
|
||||
#include "nodes/pg_list.h"
|
||||
#include "nodes/plannodes.h"
|
||||
#include "nodes/parsenodes.h"
|
||||
#include "nodes/relation.h"
|
||||
|
||||
#include "parser/catalog_utils.h"
|
||||
#include "parser/parse_query.h"
|
||||
#include "utils/elog.h"
|
||||
#include "utils/lsyscache.h"
|
||||
#include "access/heapam.h"
|
||||
|
||||
#include "optimizer/internal.h"
|
||||
#include "optimizer/planner.h"
|
||||
#include "optimizer/plancat.h"
|
||||
#include "optimizer/prep.h"
|
||||
#include "optimizer/planmain.h"
|
||||
#include "optimizer/paths.h"
|
||||
#include "optimizer/cost.h"
|
||||
|
||||
/* DATA STRUCTURE CREATION/MANIPULATION ROUTINES */
|
||||
#include "nodes/relation.h"
|
||||
#include "optimizer/clauseinfo.h"
|
||||
#include "optimizer/joininfo.h"
|
||||
#include "optimizer/keys.h"
|
||||
#include "optimizer/ordering.h"
|
||||
#include "optimizer/pathnode.h"
|
||||
#include "optimizer/clauses.h"
|
||||
#include "optimizer/tlist.h"
|
||||
#include "optimizer/var.h"
|
||||
|
||||
#include "executor/executor.h"
|
||||
|
||||
static Plan *make_sortplan(List *tlist, List *sortcls, Plan *plannode);
|
||||
static Plan *init_query_planner(Query *parse);
|
||||
static Existential *make_existential(Plan *left, Plan *right);
|
||||
|
||||
/*****************************************************************************
|
||||
*
|
||||
* Query optimizer entry point
|
||||
*
|
||||
*****************************************************************************/
|
||||
|
||||
|
||||
/*
|
||||
* planner--
|
||||
* Main query optimizer routine.
|
||||
*
|
||||
* Invokes the planner on union queries if there are any left,
|
||||
* recursing if necessary to get them all, then processes normal plans.
|
||||
*
|
||||
* Returns a query plan.
|
||||
*
|
||||
*/
|
||||
Plan*
|
||||
planner(Query *parse)
|
||||
{
|
||||
List *tlist = parse->targetList;
|
||||
List *rangetable = parse->rtable;
|
||||
char* uniqueflag = parse->uniqueFlag;
|
||||
List *sortclause = parse->sortClause;
|
||||
Plan *special_plans = (Plan*)NULL;
|
||||
|
||||
Plan *result_plan = (Plan*) NULL;
|
||||
|
||||
int rt_index;
|
||||
|
||||
/*
|
||||
* plan inheritance
|
||||
*/
|
||||
rt_index = first_matching_rt_entry(rangetable, INHERITS_FLAG);
|
||||
if (rt_index != -1) {
|
||||
special_plans = (Plan *)plan_union_queries((Index)rt_index,
|
||||
parse,
|
||||
INHERITS_FLAG);
|
||||
}
|
||||
|
||||
/*
|
||||
* plan archive queries
|
||||
*/
|
||||
rt_index = first_matching_rt_entry(rangetable, ARCHIVE_FLAG);
|
||||
if (rt_index != -1) {
|
||||
special_plans = (Plan *)plan_union_queries((Index)rt_index,
|
||||
parse,
|
||||
ARCHIVE_FLAG);
|
||||
}
|
||||
|
||||
if (special_plans)
|
||||
result_plan = special_plans;
|
||||
else
|
||||
result_plan = init_query_planner(parse); /* regular plans */
|
||||
|
||||
/*
|
||||
* For now, before we hand back the plan, check to see if there
|
||||
* is a user-specified sort that needs to be done. Eventually, this
|
||||
* will be moved into the guts of the planner s.t. user specified
|
||||
* sorts will be considered as part of the planning process.
|
||||
* Since we can only make use of user-specified sorts in
|
||||
* special cases, we can do the optimization step later.
|
||||
*/
|
||||
|
||||
if (uniqueflag) {
|
||||
Plan *sortplan = make_sortplan(tlist, sortclause, result_plan);
|
||||
|
||||
return((Plan*)make_unique(tlist,sortplan,uniqueflag));
|
||||
} else {
|
||||
if (sortclause)
|
||||
return(make_sortplan(tlist,sortclause,result_plan));
|
||||
else
|
||||
return((Plan*)result_plan);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/*
|
||||
* make_sortplan--
|
||||
* Returns a sortplan which is basically a SORT node attached to the
|
||||
* top of the plan returned from the planner. It also adds the
|
||||
* cost of sorting into the plan.
|
||||
*
|
||||
* sortkeys: ( resdom1 resdom2 resdom3 ...)
|
||||
* sortops: (sortop1 sortop2 sortop3 ...)
|
||||
*/
|
||||
static Plan *
|
||||
make_sortplan(List *tlist, List *sortcls, Plan *plannode)
|
||||
{
|
||||
Plan *sortplan = (Plan*)NULL;
|
||||
List *temp_tlist = NIL;
|
||||
List *i = NIL;
|
||||
Resdom *resnode = (Resdom*)NULL;
|
||||
Resdom *resdom = (Resdom*)NULL;
|
||||
int keyno =1;
|
||||
|
||||
/* First make a copy of the tlist so that we don't corrupt the
|
||||
* the original .
|
||||
*/
|
||||
|
||||
temp_tlist = new_unsorted_tlist(tlist);
|
||||
|
||||
foreach (i, sortcls) {
|
||||
SortClause *sortcl = (SortClause*)lfirst(i);
|
||||
|
||||
resnode = sortcl->resdom;
|
||||
resdom = tlist_resdom(temp_tlist, resnode);
|
||||
|
||||
/* Order the resdom keys and replace the operator OID for each
|
||||
* key with the regproc OID.
|
||||
*/
|
||||
resdom->reskey = keyno;
|
||||
resdom->reskeyop = get_opcode(sortcl->opoid);
|
||||
keyno += 1;
|
||||
}
|
||||
|
||||
sortplan = (Plan*)make_sort(temp_tlist,
|
||||
_TEMP_RELATION_ID_,
|
||||
(Plan*)plannode,
|
||||
length(sortcls));
|
||||
|
||||
/*
|
||||
* XXX Assuming that an internal sort has no. cost.
|
||||
* This is wrong, but given that at this point, we don't
|
||||
* know the no. of tuples returned, etc, we can't do
|
||||
* better than to add a constant cost.
|
||||
* This will be fixed once we move the sort further into the planner,
|
||||
* but for now ... functionality....
|
||||
*/
|
||||
|
||||
sortplan->cost = plannode->cost;
|
||||
|
||||
return(sortplan);
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* init-query-planner--
|
||||
* Deals with all non-union preprocessing, including existential
|
||||
* qualifications and CNFifying the qualifications.
|
||||
*
|
||||
* Returns a query plan.
|
||||
* MODIFIES: tlist,qual
|
||||
*
|
||||
*/
|
||||
static Plan *
|
||||
init_query_planner(Query *root)
|
||||
{
|
||||
List *primary_qual;
|
||||
List *existential_qual;
|
||||
Existential *exist_plan;
|
||||
List *tlist = root->targetList;
|
||||
|
||||
tlist = preprocess_targetlist(tlist,
|
||||
root->commandType,
|
||||
root->resultRelation,
|
||||
root->rtable);
|
||||
|
||||
primary_qual =
|
||||
preprocess_qualification((Expr*)root->qual,
|
||||
tlist,
|
||||
&existential_qual);
|
||||
|
||||
if(existential_qual==NULL) {
|
||||
return(query_planner(root,
|
||||
root->commandType,
|
||||
tlist,
|
||||
primary_qual));
|
||||
} else {
|
||||
int temp = root->commandType;
|
||||
Plan *existential_plan;
|
||||
|
||||
root->commandType = CMD_SELECT;
|
||||
existential_plan = query_planner(root,
|
||||
temp,
|
||||
NIL,
|
||||
existential_qual);
|
||||
|
||||
exist_plan = make_existential(existential_plan,
|
||||
query_planner(root,
|
||||
root->commandType,
|
||||
tlist,
|
||||
primary_qual));
|
||||
return((Plan*)exist_plan);
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* make_existential--
|
||||
* Instantiates an existential plan node and fills in
|
||||
* the left and right subtree slots.
|
||||
*/
|
||||
static Existential *
|
||||
make_existential(Plan *left, Plan *right)
|
||||
{
|
||||
Existential *node = makeNode(Existential);
|
||||
|
||||
node->lefttree = left;
|
||||
node->righttree = left;
|
||||
return(node);
|
||||
}
|
||||
|
||||
/*
|
||||
* pg_checkretval() -- check return value of a list of sql parse
|
||||
* trees.
|
||||
*
|
||||
* The return value of a sql function is the value returned by
|
||||
* the final query in the function. We do some ad-hoc define-time
|
||||
* type checking here to be sure that the user is returning the
|
||||
* type he claims.
|
||||
*/
|
||||
void
|
||||
pg_checkretval(Oid rettype, QueryTreeList *queryTreeList)
|
||||
{
|
||||
Query *parse;
|
||||
List *tlist;
|
||||
List *rt;
|
||||
int cmd;
|
||||
Type typ;
|
||||
Resdom *resnode;
|
||||
Relation reln;
|
||||
Oid relid;
|
||||
Oid tletype;
|
||||
int relnatts;
|
||||
int i;
|
||||
|
||||
/* find the final query */
|
||||
parse = queryTreeList->qtrees[queryTreeList->len - 1];
|
||||
|
||||
/*
|
||||
* test 1: if the last query is a utility invocation, then there
|
||||
* had better not be a return value declared.
|
||||
*/
|
||||
if (parse->commandType == CMD_UTILITY) {
|
||||
if (rettype == InvalidOid)
|
||||
return;
|
||||
else
|
||||
elog(WARN, "return type mismatch in function decl: final query is a catalog utility");
|
||||
}
|
||||
|
||||
/* okay, it's an ordinary query */
|
||||
tlist = parse->targetList;
|
||||
rt = parse->rtable;
|
||||
cmd = parse->commandType;
|
||||
|
||||
/*
|
||||
* test 2: if the function is declared to return no value, then the
|
||||
* final query had better not be a retrieve.
|
||||
*/
|
||||
if (rettype == InvalidOid) {
|
||||
if (cmd == CMD_SELECT)
|
||||
elog(WARN,
|
||||
"function declared with no return type, but final query is a retrieve");
|
||||
else
|
||||
return;
|
||||
}
|
||||
|
||||
/* by here, the function is declared to return some type */
|
||||
if ((typ = (Type)get_id_type(rettype)) == NULL)
|
||||
elog(WARN, "can't find return type %d for function\n", rettype);
|
||||
|
||||
/*
|
||||
* test 3: if the function is declared to return a value, then the
|
||||
* final query had better be a retrieve.
|
||||
*/
|
||||
if (cmd != CMD_SELECT)
|
||||
elog(WARN, "function declared to return type %s, but final query is not a retrieve", tname(typ));
|
||||
|
||||
/*
|
||||
* test 4: for base type returns, the target list should have exactly
|
||||
* one entry, and its type should agree with what the user declared.
|
||||
*/
|
||||
|
||||
if (get_typrelid(typ) == InvalidOid) {
|
||||
if (exec_tlist_length(tlist) > 1)
|
||||
elog(WARN, "function declared to return %s returns multiple values in final retrieve", tname(typ));
|
||||
|
||||
resnode = (Resdom*) ((TargetEntry*)lfirst(tlist))->resdom;
|
||||
if (resnode->restype != rettype)
|
||||
elog(WARN, "return type mismatch in function: declared to return %s, returns %s", tname(typ), tname(get_id_type(resnode->restype)));
|
||||
|
||||
/* by here, base return types match */
|
||||
return;
|
||||
}
|
||||
|
||||
/*
|
||||
* If the target list is of length 1, and the type of the varnode
|
||||
* in the target list is the same as the declared return type, this
|
||||
* is okay. This can happen, for example, where the body of the
|
||||
* function is 'retrieve (x = func2())', where func2 has the same
|
||||
* return type as the function that's calling it.
|
||||
*/
|
||||
if (exec_tlist_length(tlist) == 1) {
|
||||
resnode = (Resdom*) ((TargetEntry*)lfirst(tlist))->resdom;
|
||||
if (resnode->restype == rettype)
|
||||
return;
|
||||
}
|
||||
|
||||
/*
|
||||
* By here, the procedure returns a (set of) tuples. This part of
|
||||
* the typechecking is a hack. We look up the relation that is
|
||||
* the declared return type, and be sure that attributes 1 .. n
|
||||
* in the target list match the declared types.
|
||||
*/
|
||||
reln = heap_open(get_typrelid(typ));
|
||||
|
||||
if (!RelationIsValid(reln))
|
||||
elog(WARN, "cannot open relation relid %d", get_typrelid(typ));
|
||||
|
||||
relid = reln->rd_id;
|
||||
relnatts = reln->rd_rel->relnatts;
|
||||
|
||||
if (exec_tlist_length(tlist) != relnatts)
|
||||
elog(WARN, "function declared to return type %s does not retrieve (%s.*)", tname(typ), tname(typ));
|
||||
|
||||
/* expect attributes 1 .. n in order */
|
||||
for (i = 1; i <= relnatts; i++) {
|
||||
TargetEntry *tle = lfirst(tlist);
|
||||
Node *thenode = tle->expr;
|
||||
|
||||
tlist = lnext(tlist);
|
||||
tletype = exprType(thenode);
|
||||
|
||||
#if 0 /* fix me */
|
||||
/* this is tedious */
|
||||
if (IsA(thenode,Var))
|
||||
tletype = (Oid) ((Var*)thenode)->vartype;
|
||||
else if (IsA(thenode,Const))
|
||||
tletype = (Oid) ((Const*)thenode)->consttype;
|
||||
else if (IsA(thenode,Param)) {
|
||||
tletype = (Oid) ((Param*)thenode)->paramtype;
|
||||
else if (IsA(thenode,Expr)) {
|
||||
tletype = Expr
|
||||
}
|
||||
} else if (IsA(thenode,LispList)) {
|
||||
thenode = lfirst(thenode);
|
||||
if (IsA(thenode,Oper))
|
||||
tletype = (Oid) get_opresulttype((Oper*)thenode);
|
||||
else if (IsA(thenode,Func))
|
||||
tletype = (Oid) get_functype((Func*)thenode);
|
||||
else
|
||||
elog(WARN, "function declared to return type %s does not retrieve (%s.all)", tname(typ), tname(typ));
|
||||
#endif
|
||||
/*
|
||||
} else
|
||||
elog(WARN, "function declared to return type %s does not retrieve (%s.all)", tname(typ), tname(typ));
|
||||
*/
|
||||
/* reach right in there, why don't you? */
|
||||
if (tletype != reln->rd_att->attrs[i-1]->atttypid)
|
||||
elog(WARN, "function declared to return type %s does not retrieve (%s.all)", tname(typ), tname(typ));
|
||||
}
|
||||
|
||||
heap_close(reln);
|
||||
|
||||
/* success */
|
||||
return;
|
||||
}
|
706
src/backend/optimizer/plan/setrefs.c
Normal file
706
src/backend/optimizer/plan/setrefs.c
Normal file
@@ -0,0 +1,706 @@
|
||||
/*-------------------------------------------------------------------------
|
||||
*
|
||||
* setrefs.c--
|
||||
* Routines to change varno/attno entries to contain references
|
||||
*
|
||||
* Copyright (c) 1994, Regents of the University of California
|
||||
*
|
||||
*
|
||||
* IDENTIFICATION
|
||||
* $Header: /cvsroot/pgsql/src/backend/optimizer/plan/setrefs.c,v 1.1.1.1 1996/07/09 06:21:37 scrappy Exp $
|
||||
*
|
||||
*-------------------------------------------------------------------------
|
||||
*/
|
||||
#include "postgres.h"
|
||||
|
||||
#include "nodes/pg_list.h"
|
||||
#include "nodes/plannodes.h"
|
||||
#include "nodes/primnodes.h"
|
||||
#include "nodes/relation.h"
|
||||
|
||||
#include "utils/elog.h"
|
||||
#include "nodes/nodeFuncs.h"
|
||||
#include "nodes/makefuncs.h"
|
||||
|
||||
#include "optimizer/internal.h"
|
||||
#include "optimizer/clauses.h"
|
||||
#include "optimizer/clauseinfo.h"
|
||||
#include "optimizer/keys.h"
|
||||
#include "optimizer/planmain.h"
|
||||
#include "optimizer/tlist.h"
|
||||
#include "optimizer/var.h"
|
||||
#include "optimizer/tlist.h"
|
||||
|
||||
static void set_join_tlist_references(Join *join);
|
||||
static void set_tempscan_tlist_references(SeqScan *tempscan);
|
||||
static void set_temp_tlist_references(Temp *temp);
|
||||
static List *replace_clause_joinvar_refs(Expr *clause,
|
||||
List *outer_tlist, List *inner_tlist);
|
||||
static List *replace_subclause_joinvar_refs(List *clauses,
|
||||
List *outer_tlist, List *inner_tlist);
|
||||
static Var *replace_joinvar_refs(Var *var, List *outer_tlist, List *inner_tlist);
|
||||
static List *tlist_temp_references(Oid tempid, List *tlist);
|
||||
static void replace_result_clause(List *clause, List *subplanTargetList);
|
||||
static bool OperandIsInner(Node *opnd, int inner_relid);
|
||||
static void replace_agg_clause(Node *expr, List *targetlist);
|
||||
|
||||
/*****************************************************************************
|
||||
*
|
||||
* SUBPLAN REFERENCES
|
||||
*
|
||||
*****************************************************************************/
|
||||
|
||||
/*
|
||||
* set-tlist-references--
|
||||
* Modifies the target list of nodes in a plan to reference target lists
|
||||
* at lower levels.
|
||||
*
|
||||
* 'plan' is the plan whose target list and children's target lists will
|
||||
* be modified
|
||||
*
|
||||
* Returns nothing of interest, but modifies internal fields of nodes.
|
||||
*
|
||||
*/
|
||||
void
|
||||
set_tlist_references(Plan *plan)
|
||||
{
|
||||
if(plan==NULL)
|
||||
return;
|
||||
|
||||
if (IsA_Join(plan)) {
|
||||
set_join_tlist_references((Join*)plan);
|
||||
} else if (IsA(plan,SeqScan) && plan->lefttree &&
|
||||
IsA_Temp(plan->lefttree)) {
|
||||
set_tempscan_tlist_references((SeqScan*)plan);
|
||||
} else if (IsA(plan,Sort)) {
|
||||
set_temp_tlist_references ((Temp*)plan);
|
||||
} else if (IsA(plan,Result)) {
|
||||
set_result_tlist_references((Result*)plan);
|
||||
} else if (IsA(plan,Hash)) {
|
||||
set_tlist_references(plan->lefttree);
|
||||
} else if (IsA(plan,Choose)) {
|
||||
List *x;
|
||||
foreach (x, ((Choose*)plan)->chooseplanlist) {
|
||||
set_tlist_references((Plan*)lfirst(x));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* set-join-tlist-references--
|
||||
* Modifies the target list of a join node by setting the varnos and
|
||||
* varattnos to reference the target list of the outer and inner join
|
||||
* relations.
|
||||
*
|
||||
* Creates a target list for a join node to contain references by setting
|
||||
* varno values to OUTER or INNER and setting attno values to the
|
||||
* result domain number of either the corresponding outer or inner join
|
||||
* tuple.
|
||||
*
|
||||
* 'join' is a join plan node
|
||||
*
|
||||
* Returns nothing of interest, but modifies internal fields of nodes.
|
||||
*
|
||||
*/
|
||||
static void
|
||||
set_join_tlist_references(Join *join)
|
||||
{
|
||||
Plan *outer = ((Plan*)join)->lefttree;
|
||||
Plan *inner = ((Plan*)join)->righttree;
|
||||
List *new_join_targetlist = NIL;
|
||||
TargetEntry *temp = (TargetEntry *)NULL;
|
||||
List *entry = NIL;
|
||||
List *inner_tlist = NULL;
|
||||
List *outer_tlist = NULL;
|
||||
TargetEntry *xtl = (TargetEntry *)NULL;
|
||||
List *qptlist = ((Plan*)join)->targetlist;
|
||||
|
||||
foreach(entry, qptlist) {
|
||||
List *joinvar;
|
||||
|
||||
xtl = (TargetEntry *)lfirst(entry);
|
||||
inner_tlist = ((inner==NULL) ? NIL : inner->targetlist);
|
||||
outer_tlist = ((outer==NULL) ? NIL : outer->targetlist);
|
||||
joinvar = replace_clause_joinvar_refs((Expr*)get_expr(xtl),
|
||||
outer_tlist,
|
||||
inner_tlist);
|
||||
|
||||
temp = MakeTLE(xtl->resdom, (Node*)joinvar);
|
||||
new_join_targetlist = lappend(new_join_targetlist,temp);
|
||||
}
|
||||
|
||||
((Plan*)join)->targetlist = new_join_targetlist;
|
||||
if (outer!=NULL)
|
||||
set_tlist_references(outer);
|
||||
if (inner!=NULL)
|
||||
set_tlist_references(inner);
|
||||
}
|
||||
|
||||
/*
|
||||
* set-tempscan-tlist-references--
|
||||
* Modifies the target list of a node that scans a temp relation (i.e., a
|
||||
* sort or hash node) so that the varnos refer to the child temporary.
|
||||
*
|
||||
* 'tempscan' is a seqscan node
|
||||
*
|
||||
* Returns nothing of interest, but modifies internal fields of nodes.
|
||||
*
|
||||
*/
|
||||
static void
|
||||
set_tempscan_tlist_references(SeqScan *tempscan)
|
||||
{
|
||||
Temp *temp = (Temp*)((Plan*)tempscan)->lefttree;
|
||||
|
||||
((Plan*)tempscan)->targetlist =
|
||||
tlist_temp_references(temp->tempid,
|
||||
((Plan*)tempscan)->targetlist);
|
||||
set_temp_tlist_references(temp);
|
||||
}
|
||||
|
||||
/*
|
||||
* set-temp-tlist-references--
|
||||
* The temp's vars are made consistent with (actually, identical to) the
|
||||
* modified version of the target list of the node from which temp node
|
||||
* receives its tuples.
|
||||
*
|
||||
* 'temp' is a temp (e.g., sort, hash) plan node
|
||||
*
|
||||
* Returns nothing of interest, but modifies internal fields of nodes.
|
||||
*
|
||||
*/
|
||||
static void
|
||||
set_temp_tlist_references(Temp *temp)
|
||||
{
|
||||
Plan *source = ((Plan*)temp)->lefttree;
|
||||
|
||||
if (source!=NULL) {
|
||||
set_tlist_references(source);
|
||||
((Plan*)temp)->targetlist =
|
||||
copy_vars(((Plan*)temp)->targetlist ,
|
||||
(source)->targetlist);
|
||||
} else {
|
||||
elog(WARN, "calling set_temp_tlist_references with empty lefttree");
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* join-references--
|
||||
* Creates a new set of join clauses by replacing the varno/varattno
|
||||
* values of variables in the clauses to reference target list values
|
||||
* from the outer and inner join relation target lists.
|
||||
*
|
||||
* 'clauses' is the list of join clauses
|
||||
* 'outer-tlist' is the target list of the outer join relation
|
||||
* 'inner-tlist' is the target list of the inner join relation
|
||||
*
|
||||
* Returns the new join clauses.
|
||||
*
|
||||
*/
|
||||
List *
|
||||
join_references(List *clauses,
|
||||
List *outer_tlist,
|
||||
List *inner_tlist)
|
||||
{
|
||||
return (replace_subclause_joinvar_refs(clauses,
|
||||
outer_tlist,
|
||||
inner_tlist));
|
||||
}
|
||||
|
||||
/*
|
||||
* index-outerjoin-references--
|
||||
* Given a list of join clauses, replace the operand corresponding to the
|
||||
* outer relation in the join with references to the corresponding target
|
||||
* list element in 'outer-tlist' (the outer is rather obscurely
|
||||
* identified as the side that doesn't contain a var whose varno equals
|
||||
* 'inner-relid').
|
||||
*
|
||||
* As a side effect, the operator is replaced by the regproc id.
|
||||
*
|
||||
* 'inner-indxqual' is the list of join clauses (so-called because they
|
||||
* are used as qualifications for the inner (inbex) scan of a nestloop)
|
||||
*
|
||||
* Returns the new list of clauses.
|
||||
*
|
||||
*/
|
||||
List *
|
||||
index_outerjoin_references(List *inner_indxqual,
|
||||
List *outer_tlist,
|
||||
Index inner_relid)
|
||||
{
|
||||
List *t_list = NIL;
|
||||
Expr *temp = NULL;
|
||||
List *t_clause = NIL;
|
||||
Expr *clause = NULL;
|
||||
|
||||
foreach (t_clause,inner_indxqual) {
|
||||
clause = lfirst(t_clause);
|
||||
/*
|
||||
* if inner scan on the right.
|
||||
*/
|
||||
if (OperandIsInner((Node*)get_rightop(clause), inner_relid)) {
|
||||
Var *joinvar = (Var*)
|
||||
replace_clause_joinvar_refs((Expr*)get_leftop(clause),
|
||||
outer_tlist,
|
||||
NIL);
|
||||
temp = make_opclause(replace_opid((Oper*)((Expr*)clause)->oper),
|
||||
joinvar,
|
||||
get_rightop(clause));
|
||||
t_list = lappend(t_list,temp);
|
||||
} else {
|
||||
/* inner scan on left */
|
||||
Var *joinvar = (Var*)
|
||||
replace_clause_joinvar_refs((Expr*)get_rightop(clause),
|
||||
outer_tlist,
|
||||
NIL);
|
||||
temp = make_opclause(replace_opid((Oper*)((Expr*)clause)->oper),
|
||||
joinvar,
|
||||
get_leftop(clause));
|
||||
t_list = lappend(t_list,temp);
|
||||
}
|
||||
|
||||
}
|
||||
return(t_list);
|
||||
}
|
||||
|
||||
/*
|
||||
* replace-clause-joinvar-refs
|
||||
* replace-subclause-joinvar-refs
|
||||
* replace-joinvar-refs
|
||||
*
|
||||
* Replaces all variables within a join clause with a new var node
|
||||
* whose varno/varattno fields contain a reference to a target list
|
||||
* element from either the outer or inner join relation.
|
||||
*
|
||||
* 'clause' is the join clause
|
||||
* 'outer-tlist' is the target list of the outer join relation
|
||||
* 'inner-tlist' is the target list of the inner join relation
|
||||
*
|
||||
* Returns the new join clause.
|
||||
*
|
||||
*/
|
||||
static List *
|
||||
replace_clause_joinvar_refs(Expr *clause,
|
||||
List *outer_tlist,
|
||||
List *inner_tlist)
|
||||
{
|
||||
List *temp = NULL;
|
||||
|
||||
if(IsA (clause,Var)) {
|
||||
temp = (List*)replace_joinvar_refs((Var*)clause,
|
||||
outer_tlist,inner_tlist);
|
||||
if(temp)
|
||||
return(temp);
|
||||
else
|
||||
if (clause != NULL)
|
||||
return((List*)clause);
|
||||
else
|
||||
return(NIL);
|
||||
} else if (single_node((Node*)clause)) {
|
||||
return ((List*)clause);
|
||||
} else if (or_clause((Node*)clause)) {
|
||||
List *orclause =
|
||||
replace_subclause_joinvar_refs(((Expr*)clause)->args,
|
||||
outer_tlist,
|
||||
inner_tlist);
|
||||
return ((List*)make_orclause(orclause));
|
||||
} else if (IsA(clause,ArrayRef)) {
|
||||
ArrayRef *aref = (ArrayRef *)clause;
|
||||
|
||||
temp = replace_subclause_joinvar_refs(aref->refupperindexpr,
|
||||
outer_tlist,
|
||||
inner_tlist);
|
||||
aref->refupperindexpr = (List*)temp;
|
||||
temp = replace_subclause_joinvar_refs(aref->reflowerindexpr,
|
||||
outer_tlist,
|
||||
inner_tlist);
|
||||
aref->reflowerindexpr = (List*)temp;
|
||||
temp = replace_clause_joinvar_refs((Expr*)aref->refexpr,
|
||||
outer_tlist,
|
||||
inner_tlist);
|
||||
aref->refexpr = (Node*)temp;
|
||||
|
||||
/*
|
||||
* no need to set refassgnexpr. we only set that in the
|
||||
* target list on replaces, and this is an array reference
|
||||
* in the qualification. if we got this far, it's 0x0 in
|
||||
* the ArrayRef structure 'clause'.
|
||||
*/
|
||||
|
||||
return((List*)clause);
|
||||
} else if (is_funcclause((Node*)clause)) {
|
||||
List *funcclause =
|
||||
replace_subclause_joinvar_refs(((Expr*)clause)->args,
|
||||
outer_tlist,
|
||||
inner_tlist);
|
||||
return ((List*)make_funcclause((Func*)((Expr*)clause)->oper,
|
||||
funcclause));
|
||||
} else if (not_clause((Node*)clause)) {
|
||||
List *notclause =
|
||||
replace_clause_joinvar_refs(get_notclausearg(clause),
|
||||
outer_tlist,
|
||||
inner_tlist);
|
||||
return ((List*)make_notclause((Expr*)notclause));
|
||||
} else if (is_opclause((Node*)clause)) {
|
||||
Var *leftvar =
|
||||
(Var*)replace_clause_joinvar_refs((Expr*)get_leftop(clause),
|
||||
outer_tlist,
|
||||
inner_tlist);
|
||||
Var *rightvar =
|
||||
(Var*)replace_clause_joinvar_refs((Expr*)get_rightop(clause),
|
||||
outer_tlist,
|
||||
inner_tlist);
|
||||
return ((List*)make_opclause(replace_opid((Oper*)((Expr*)clause)->oper),
|
||||
leftvar,
|
||||
rightvar));
|
||||
}
|
||||
/* shouldn't reach here */
|
||||
return NULL;
|
||||
}
|
||||
|
||||
static List *
|
||||
replace_subclause_joinvar_refs(List *clauses,
|
||||
List *outer_tlist,
|
||||
List *inner_tlist)
|
||||
{
|
||||
List *t_list = NIL;
|
||||
List *temp = NIL;
|
||||
List *clause = NIL;
|
||||
|
||||
foreach (clause,clauses) {
|
||||
temp = replace_clause_joinvar_refs(lfirst(clause),
|
||||
outer_tlist,
|
||||
inner_tlist);
|
||||
t_list = lappend(t_list,temp);
|
||||
}
|
||||
return(t_list);
|
||||
}
|
||||
|
||||
static Var *
|
||||
replace_joinvar_refs(Var *var, List *outer_tlist, List *inner_tlist)
|
||||
{
|
||||
Resdom *outer_resdom =(Resdom*)NULL;
|
||||
|
||||
outer_resdom= tlist_member(var,outer_tlist);
|
||||
|
||||
if (outer_resdom!=NULL && IsA (outer_resdom,Resdom) ) {
|
||||
return (makeVar (OUTER,
|
||||
outer_resdom->resno,
|
||||
var->vartype,
|
||||
var->varnoold,
|
||||
var->varoattno));
|
||||
} else {
|
||||
Resdom *inner_resdom;
|
||||
inner_resdom = tlist_member(var,inner_tlist);
|
||||
if ( inner_resdom!=NULL && IsA (inner_resdom,Resdom) ) {
|
||||
return (makeVar (INNER,
|
||||
inner_resdom->resno,
|
||||
var->vartype,
|
||||
var->varnoold,
|
||||
var->varoattno));
|
||||
}
|
||||
}
|
||||
return (Var*)NULL;
|
||||
}
|
||||
|
||||
/*
|
||||
* tlist-temp-references--
|
||||
* Creates a new target list for a node that scans a temp relation,
|
||||
* setting the varnos to the id of the temp relation and setting varids
|
||||
* if necessary (varids are only needed if this is a targetlist internal
|
||||
* to the tree, in which case the targetlist entry always contains a var
|
||||
* node, so we can just copy it from the temp).
|
||||
*
|
||||
* 'tempid' is the id of the temp relation
|
||||
* 'tlist' is the target list to be modified
|
||||
*
|
||||
* Returns new target list
|
||||
*
|
||||
*/
|
||||
static List *
|
||||
tlist_temp_references(Oid tempid,
|
||||
List *tlist)
|
||||
{
|
||||
List *t_list = NIL;
|
||||
TargetEntry *temp = (TargetEntry *)NULL;
|
||||
TargetEntry *xtl = NULL;
|
||||
List *entry;
|
||||
|
||||
foreach (entry, tlist) {
|
||||
AttrNumber oattno;
|
||||
|
||||
xtl = lfirst(entry);
|
||||
if (IsA(get_expr(xtl), Var))
|
||||
oattno = ((Var*)xtl->expr)->varoattno;
|
||||
else
|
||||
oattno = 0;
|
||||
|
||||
temp = MakeTLE(xtl->resdom,
|
||||
(Node*)makeVar(tempid,
|
||||
xtl->resdom->resno,
|
||||
xtl->resdom->restype,
|
||||
tempid,
|
||||
oattno));
|
||||
|
||||
t_list = lappend(t_list,temp);
|
||||
}
|
||||
return(t_list);
|
||||
}
|
||||
|
||||
/*---------------------------------------------------------
|
||||
*
|
||||
* set_result_tlist_references
|
||||
*
|
||||
* Change the target list of a Result node, so that it correctly
|
||||
* addresses the tuples returned by its left tree subplan.
|
||||
*
|
||||
* NOTE:
|
||||
* 1) we ignore the right tree! (in the current implementation
|
||||
* it is always nil
|
||||
* 2) this routine will probably *NOT* work with nested dot
|
||||
* fields....
|
||||
*/
|
||||
void
|
||||
set_result_tlist_references(Result *resultNode)
|
||||
{
|
||||
Plan *subplan;
|
||||
List *resultTargetList;
|
||||
List *subplanTargetList;
|
||||
List *t;
|
||||
TargetEntry *entry;
|
||||
Expr *expr;
|
||||
|
||||
resultTargetList= ((Plan*)resultNode)->targetlist;
|
||||
|
||||
/*
|
||||
* NOTE: we only consider the left tree subplan.
|
||||
* This is usually a seq scan.
|
||||
*/
|
||||
subplan = ((Plan*)resultNode)->lefttree;
|
||||
if (subplan != NULL) {
|
||||
subplanTargetList = subplan->targetlist;
|
||||
} else {
|
||||
subplanTargetList = NIL;
|
||||
}
|
||||
|
||||
/*
|
||||
* now for traverse all the entris of the target list.
|
||||
* These should be of the form (Resdom_Node Expression).
|
||||
* For every expression clause, call "replace_result_clause()"
|
||||
* to appropriatelly change all the Var nodes.
|
||||
*/
|
||||
foreach (t, resultTargetList) {
|
||||
entry = (TargetEntry *)lfirst(t);
|
||||
expr = (Expr*) get_expr(entry);
|
||||
replace_result_clause((List*)expr, subplanTargetList);
|
||||
}
|
||||
}
|
||||
|
||||
/*---------------------------------------------------------
|
||||
*
|
||||
* replace_result_clause
|
||||
*
|
||||
* This routine is called from set_result_tlist_references().
|
||||
* and modifies the expressions of the target list of a Result
|
||||
* node so that all Var nodes reference the target list of its subplan.
|
||||
*
|
||||
*/
|
||||
static void
|
||||
replace_result_clause(List *clause,
|
||||
List *subplanTargetList) /* target list of the
|
||||
subplan */
|
||||
{
|
||||
List *t;
|
||||
List *subClause;
|
||||
TargetEntry *subplanVar;
|
||||
|
||||
if (IsA(clause,Var)) {
|
||||
/*
|
||||
* Ha! A Var node!
|
||||
*/
|
||||
subplanVar = match_varid((Var*)clause, subplanTargetList);
|
||||
/*
|
||||
* Change the varno & varattno fields of the
|
||||
* var node.
|
||||
*
|
||||
*/
|
||||
((Var*)clause)->varno = (Index)OUTER;
|
||||
((Var*)clause)->varattno = subplanVar->resdom->resno;
|
||||
} else if (is_funcclause((Node*)clause)) {
|
||||
/*
|
||||
* This is a function. Recursively call this routine
|
||||
* for its arguments...
|
||||
*/
|
||||
subClause = ((Expr*)clause)->args;
|
||||
foreach (t, subClause) {
|
||||
replace_result_clause(lfirst(t),subplanTargetList);
|
||||
}
|
||||
} else if (IsA(clause,ArrayRef)) {
|
||||
ArrayRef *aref = (ArrayRef *)clause;
|
||||
/*
|
||||
* This is an arrayref. Recursively call this routine
|
||||
* for its expression and its index expression...
|
||||
*/
|
||||
subClause = aref->refupperindexpr;
|
||||
foreach (t, subClause) {
|
||||
replace_result_clause(lfirst(t),subplanTargetList);
|
||||
}
|
||||
subClause = aref->reflowerindexpr;
|
||||
foreach (t, subClause) {
|
||||
replace_result_clause(lfirst(t),subplanTargetList);
|
||||
}
|
||||
replace_result_clause((List*)aref->refexpr,
|
||||
subplanTargetList);
|
||||
replace_result_clause((List*)aref->refassgnexpr,
|
||||
subplanTargetList);
|
||||
} else if (is_opclause((Node*)clause)) {
|
||||
/*
|
||||
* This is an operator. Recursively call this routine
|
||||
* for both its left and right operands
|
||||
*/
|
||||
subClause = (List*)get_leftop((Expr*)clause);
|
||||
replace_result_clause(subClause,subplanTargetList);
|
||||
subClause = (List*)get_rightop((Expr*)clause);
|
||||
replace_result_clause(subClause,subplanTargetList);
|
||||
} else if (IsA(clause,Param) || IsA(clause,Const)) {
|
||||
/* do nothing! */
|
||||
} else {
|
||||
/*
|
||||
* Ooops! we can not handle that!
|
||||
*/
|
||||
elog(WARN,"replace_result_clause: Can not handle this tlist!\n");
|
||||
}
|
||||
}
|
||||
|
||||
static
|
||||
bool OperandIsInner(Node *opnd, int inner_relid)
|
||||
{
|
||||
/*
|
||||
* Can be the inner scan if its a varnode or a function and the
|
||||
* inner_relid is equal to the varnode's var number or in the
|
||||
* case of a function the first argument's var number (all args
|
||||
* in a functional index are from the same relation).
|
||||
*/
|
||||
if ( IsA (opnd,Var) &&
|
||||
(inner_relid == ((Var*)opnd)->varno) )
|
||||
{
|
||||
return true;
|
||||
}
|
||||
if (is_funcclause(opnd))
|
||||
{
|
||||
List *firstArg = lfirst(((Expr*)opnd)->args);
|
||||
|
||||
if ( IsA (firstArg,Var) &&
|
||||
(inner_relid == ((Var*)firstArg)->varno) )
|
||||
{
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
/*****************************************************************************
|
||||
*
|
||||
*****************************************************************************/
|
||||
|
||||
/*---------------------------------------------------------
|
||||
*
|
||||
* set_agg_tlist_references -
|
||||
* changes the target list of an Agg node so that it points to
|
||||
* the tuples returned by its left tree subplan.
|
||||
*
|
||||
*/
|
||||
void
|
||||
set_agg_tlist_references(Agg *aggNode)
|
||||
{
|
||||
List *aggTargetList;
|
||||
List *subplanTargetList;
|
||||
List *tl;
|
||||
|
||||
aggTargetList = aggNode->plan.targetlist;
|
||||
subplanTargetList = aggNode->plan.lefttree->targetlist;
|
||||
|
||||
foreach (tl, aggTargetList) {
|
||||
TargetEntry *tle = lfirst(tl);
|
||||
|
||||
replace_agg_clause(tle->expr, subplanTargetList);
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
set_agg_agglist_references(Agg *aggNode)
|
||||
{
|
||||
List *subplanTargetList;
|
||||
Aggreg **aggs;
|
||||
int i;
|
||||
|
||||
aggs = aggNode->aggs;
|
||||
subplanTargetList = aggNode->plan.lefttree->targetlist;
|
||||
|
||||
for (i = 0; i < aggNode->numAgg; i++) {
|
||||
replace_agg_clause(aggs[i]->target, subplanTargetList);
|
||||
}
|
||||
}
|
||||
|
||||
static void
|
||||
replace_agg_clause(Node *clause, List *subplanTargetList)
|
||||
{
|
||||
List *t;
|
||||
TargetEntry *subplanVar;
|
||||
|
||||
if (IsA(clause,Var)) {
|
||||
/*
|
||||
* Ha! A Var node!
|
||||
*/
|
||||
subplanVar = match_varid((Var*)clause, subplanTargetList);
|
||||
/*
|
||||
* Change the varno & varattno fields of the
|
||||
* var node.
|
||||
*
|
||||
*/
|
||||
((Var*)clause)->varattno = subplanVar->resdom->resno;
|
||||
} else if (is_funcclause(clause)) {
|
||||
/*
|
||||
* This is a function. Recursively call this routine
|
||||
* for its arguments...
|
||||
*/
|
||||
foreach (t, ((Expr*)clause)->args) {
|
||||
replace_agg_clause(lfirst(t), subplanTargetList);
|
||||
}
|
||||
} else if (IsA(clause,Aggreg)) {
|
||||
replace_agg_clause(((Aggreg*)clause)->target, subplanTargetList);
|
||||
} else if (IsA(clause,ArrayRef)) {
|
||||
ArrayRef *aref = (ArrayRef *)clause;
|
||||
|
||||
/*
|
||||
* This is an arrayref. Recursively call this routine
|
||||
* for its expression and its index expression...
|
||||
*/
|
||||
foreach (t, aref->refupperindexpr) {
|
||||
replace_agg_clause(lfirst(t),subplanTargetList);
|
||||
}
|
||||
foreach (t, aref->reflowerindexpr) {
|
||||
replace_agg_clause(lfirst(t),subplanTargetList);
|
||||
}
|
||||
replace_agg_clause(aref->refexpr, subplanTargetList);
|
||||
replace_agg_clause(aref->refassgnexpr, subplanTargetList);
|
||||
} else if (is_opclause(clause)) {
|
||||
/*
|
||||
* This is an operator. Recursively call this routine
|
||||
* for both its left and right operands
|
||||
*/
|
||||
replace_agg_clause((Node*)get_leftop((Expr*)clause),
|
||||
subplanTargetList);
|
||||
replace_agg_clause((Node*)get_rightop((Expr*)clause),
|
||||
subplanTargetList);
|
||||
} else if (IsA(clause,Param) || IsA(clause,Const)) {
|
||||
/* do nothing! */
|
||||
} else {
|
||||
/*
|
||||
* Ooops! we can not handle that!
|
||||
*/
|
||||
elog(WARN,"replace_agg_clause: Can not handle this tlist!\n");
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
65
src/backend/optimizer/plancat.h
Normal file
65
src/backend/optimizer/plancat.h
Normal file
@@ -0,0 +1,65 @@
|
||||
/*-------------------------------------------------------------------------
|
||||
*
|
||||
* plancat.h--
|
||||
* prototypes for plancat.c.
|
||||
*
|
||||
*
|
||||
* Copyright (c) 1994, Regents of the University of California
|
||||
*
|
||||
* $Id: plancat.h,v 1.1.1.1 1996/07/09 06:21:34 scrappy Exp $
|
||||
*
|
||||
*-------------------------------------------------------------------------
|
||||
*/
|
||||
#ifndef PLANCAT_H
|
||||
#define PLANCAT_H
|
||||
|
||||
#include "c.h"
|
||||
|
||||
/*
|
||||
* transient data structure to hold return value of index_info. Note that
|
||||
* indexkeys, orderOprs and classlist is "null-terminated".
|
||||
*/
|
||||
typedef struct IdxInfoRetval {
|
||||
Oid relid; /* OID of the index relation (not the OID
|
||||
* of the relation being indexed)
|
||||
*/
|
||||
Oid relam; /* OID of the pg_am of this index */
|
||||
int pages; /* number of pages in the index relation */
|
||||
int tuples; /* number of tuples in the index relation */
|
||||
int *indexkeys; /* keys over which we're indexing */
|
||||
Oid *orderOprs; /* operators used for ordering purposes */
|
||||
Oid *classlist; /* classes of AM operators */
|
||||
Oid indproc;
|
||||
Node *indpred;
|
||||
} IdxInfoRetval;
|
||||
|
||||
|
||||
extern void relation_info(Query *root,
|
||||
Oid relid,
|
||||
bool *hashindex, int *pages,
|
||||
int *tuples);
|
||||
|
||||
extern bool index_info(Query *root,
|
||||
bool first, int relid, IdxInfoRetval *info);
|
||||
|
||||
extern Cost
|
||||
restriction_selectivity(Oid functionObjectId,
|
||||
Oid operatorObjectId,
|
||||
Oid relationObjectId,
|
||||
AttrNumber attributeNumber,
|
||||
char *constValue,
|
||||
int32 constFlag);
|
||||
|
||||
extern void
|
||||
index_selectivity(Oid indid, Oid *classes, List *opnos,
|
||||
Oid relid, List *attnos, List *values, List *flags,
|
||||
int32 nkeys, float *idxPages, float *idxSelec);
|
||||
|
||||
extern Cost join_selectivity(Oid functionObjectId, Oid operatorObjectId,
|
||||
Oid relationObjectId1, AttrNumber attributeNumber1,
|
||||
Oid relationObjectId2, AttrNumber attributeNumber2);
|
||||
|
||||
extern List *find_inheritance_children(Oid inhparent);
|
||||
extern List *VersionGetParents(Oid verrelid);
|
||||
|
||||
#endif /* PLANCAT_H */
|
60
src/backend/optimizer/planmain.h
Normal file
60
src/backend/optimizer/planmain.h
Normal file
@@ -0,0 +1,60 @@
|
||||
/*-------------------------------------------------------------------------
|
||||
*
|
||||
* planmain.h--
|
||||
* prototypes for various files in optimizer/plan
|
||||
*
|
||||
*
|
||||
* Copyright (c) 1994, Regents of the University of California
|
||||
*
|
||||
* $Id: planmain.h,v 1.1.1.1 1996/07/09 06:21:34 scrappy Exp $
|
||||
*
|
||||
*-------------------------------------------------------------------------
|
||||
*/
|
||||
#ifndef PLANMAIN_H
|
||||
#define PLANMAIN_H
|
||||
|
||||
|
||||
/*
|
||||
* prototypes for plan/planmain.c
|
||||
*/
|
||||
extern Plan *query_planner(Query *root,
|
||||
int command_type, List *tlist, List *qual);
|
||||
|
||||
|
||||
/*
|
||||
* prototypes for plan/createplan.c
|
||||
*/
|
||||
extern Plan *create_plan(Path *best_path);
|
||||
extern SeqScan *make_seqscan(List *qptlist, List *qpqual, Index scanrelid,
|
||||
Plan *lefttree);
|
||||
extern Sort *make_sort(List *tlist, Oid tempid, Plan *lefttree,
|
||||
int keycount);
|
||||
extern Agg *make_agg(List *tlist, int nagg, Aggreg **aggs);
|
||||
extern Group *make_group(List *tlist, bool tuplePerGroup, int ngrp,
|
||||
AttrNumber *grpColIdx, Sort *lefttree);
|
||||
extern Unique *make_unique(List *tlist, Plan *lefttree, char *uniqueAttr);
|
||||
extern List *generate_fjoin(List *tlist);
|
||||
|
||||
|
||||
/*
|
||||
* prototypes for plan/initsplan.c
|
||||
*/
|
||||
extern void initialize_base_rels_list(Query *root, List *tlist);
|
||||
extern void initialize_base_rels_jinfo(Query *root, List *clauses);
|
||||
extern void initialize_join_clause_info(List *rel_list);
|
||||
extern void add_missing_vars_to_base_rels(Query *root, List *tlist);
|
||||
|
||||
/*
|
||||
* prototypes for plan/setrefs.c
|
||||
*/
|
||||
extern void set_tlist_references(Plan *plan);
|
||||
extern List *join_references(List *clauses, List *outer_tlist,
|
||||
List *inner_tlist);
|
||||
extern List *index_outerjoin_references(List *inner_indxqual,
|
||||
List *outer_tlist, Index inner_relid);
|
||||
extern void set_result_tlist_references(Result *resultNode);
|
||||
extern void set_agg_tlist_references(Agg *aggNode);
|
||||
extern void set_agg_agglist_references(Agg *aggNode);
|
||||
|
||||
|
||||
#endif /* PLANMAIN_H */
|
24
src/backend/optimizer/planner.h
Normal file
24
src/backend/optimizer/planner.h
Normal file
@@ -0,0 +1,24 @@
|
||||
/*-------------------------------------------------------------------------
|
||||
*
|
||||
* planner.h--
|
||||
* prototypes for planner.c.
|
||||
*
|
||||
*
|
||||
* Copyright (c) 1994, Regents of the University of California
|
||||
*
|
||||
* $Id: planner.h,v 1.1.1.1 1996/07/09 06:21:34 scrappy Exp $
|
||||
*
|
||||
*-------------------------------------------------------------------------
|
||||
*/
|
||||
#ifndef PLANNER_H
|
||||
#define PLANNER_H
|
||||
|
||||
/*
|
||||
#include "optimizer/internal.h"
|
||||
#include "parser/parse_query.h"
|
||||
*/
|
||||
|
||||
extern Plan *planner(Query *parse);
|
||||
extern void pg_checkretval(Oid rettype, QueryTreeList *querytree_list);
|
||||
|
||||
#endif /* PLANNER_H */
|
51
src/backend/optimizer/prep.h
Normal file
51
src/backend/optimizer/prep.h
Normal file
@@ -0,0 +1,51 @@
|
||||
/*-------------------------------------------------------------------------
|
||||
*
|
||||
* prep.h--
|
||||
* prototypes for files in prep.c
|
||||
*
|
||||
*
|
||||
* Copyright (c) 1994, Regents of the University of California
|
||||
*
|
||||
* $Id: prep.h,v 1.1.1.1 1996/07/09 06:21:34 scrappy Exp $
|
||||
*
|
||||
*-------------------------------------------------------------------------
|
||||
*/
|
||||
#ifndef PREP_H
|
||||
#define PREP_H
|
||||
|
||||
#include "nodes/primnodes.h"
|
||||
#include "nodes/plannodes.h"
|
||||
|
||||
/*
|
||||
* prototypes for archive.h
|
||||
*/
|
||||
extern void plan_archive(List *rt);
|
||||
extern List *find_archive_rels(Oid relid);
|
||||
|
||||
/*
|
||||
* prototypes for prepqual.h
|
||||
*/
|
||||
extern List *preprocess_qualification(Expr *qual, List *tlist,
|
||||
List **existentialQualPtr);
|
||||
extern List *cnfify(Expr *qual, bool removeAndFlag);
|
||||
|
||||
/*
|
||||
* prototypes for preptlist.h
|
||||
*/
|
||||
extern List *preprocess_targetlist(List *tlist, int command_type,
|
||||
Index result_relation, List *range_table);
|
||||
|
||||
/*
|
||||
* prototypes for prepunion.h
|
||||
*/
|
||||
typedef enum UnionFlag {
|
||||
INHERITS_FLAG, ARCHIVE_FLAG, VERSION_FLAG
|
||||
} UnionFlag;
|
||||
|
||||
extern List *find_all_inheritors(List *unexamined_relids,
|
||||
List *examined_relids);
|
||||
extern int first_matching_rt_entry(List *rangetable, UnionFlag flag);
|
||||
extern Append *plan_union_queries(Index rt_index, Query *parse,
|
||||
UnionFlag flag);
|
||||
|
||||
#endif /* PREP_H */
|
14
src/backend/optimizer/prep/Makefile.inc
Normal file
14
src/backend/optimizer/prep/Makefile.inc
Normal file
@@ -0,0 +1,14 @@
|
||||
#-------------------------------------------------------------------------
|
||||
#
|
||||
# Makefile.inc--
|
||||
# Makefile for optimizer/prep
|
||||
#
|
||||
# Copyright (c) 1994, Regents of the University of California
|
||||
#
|
||||
#
|
||||
# IDENTIFICATION
|
||||
# $Header: /cvsroot/pgsql/src/backend/optimizer/prep/Attic/Makefile.inc,v 1.1.1.1 1996/07/09 06:21:37 scrappy Exp $
|
||||
#
|
||||
#-------------------------------------------------------------------------
|
||||
|
||||
SUBSRCS+= archive.c prepqual.c preptlist.c prepunion.c
|
66
src/backend/optimizer/prep/archive.c
Normal file
66
src/backend/optimizer/prep/archive.c
Normal file
@@ -0,0 +1,66 @@
|
||||
/*-------------------------------------------------------------------------
|
||||
*
|
||||
* archive.c--
|
||||
* Support for planning scans on archived relations
|
||||
*
|
||||
* Copyright (c) 1994, Regents of the University of California
|
||||
*
|
||||
*
|
||||
* IDENTIFICATION
|
||||
* $Header: /cvsroot/pgsql/src/backend/optimizer/prep/Attic/archive.c,v 1.1.1.1 1996/07/09 06:21:38 scrappy Exp $
|
||||
*
|
||||
*-------------------------------------------------------------------------
|
||||
*/
|
||||
#include <stdio.h> /* for sprintf() */
|
||||
#include <sys/types.h> /* for u_int in relcache.h */
|
||||
#include "postgres.h"
|
||||
|
||||
#include "utils/rel.h"
|
||||
#include "utils/elog.h"
|
||||
#include "utils/palloc.h"
|
||||
#include "utils/relcache.h"
|
||||
#include "catalog/pg_class.h"
|
||||
#include "nodes/pg_list.h"
|
||||
#include "nodes/parsenodes.h"
|
||||
#include "optimizer/prep.h"
|
||||
#include "commands/creatinh.h"
|
||||
|
||||
void
|
||||
plan_archive(List *rt)
|
||||
{
|
||||
List *rtitem;
|
||||
RangeTblEntry *rte;
|
||||
TimeRange *trange;
|
||||
Relation r;
|
||||
Oid reloid;
|
||||
|
||||
foreach(rtitem, rt) {
|
||||
rte = lfirst(rtitem);
|
||||
trange = rte->timeRange;
|
||||
if (trange) {
|
||||
reloid = rte->relid;
|
||||
r = RelationIdGetRelation(reloid);
|
||||
if (r->rd_rel->relarch != 'n') {
|
||||
rte->archive = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* find_archive_rels -- Given a particular relid, find the archive
|
||||
* relation's relid.
|
||||
*/
|
||||
List *
|
||||
find_archive_rels(Oid relid)
|
||||
{
|
||||
Relation arel;
|
||||
char *arelName;
|
||||
|
||||
arelName = MakeArchiveName(relid);
|
||||
arel = RelationNameGetRelation(arelName);
|
||||
pfree(arelName);
|
||||
|
||||
return lconsi(arel->rd_id, lconsi(relid, NIL));
|
||||
}
|
582
src/backend/optimizer/prep/prepqual.c
Normal file
582
src/backend/optimizer/prep/prepqual.c
Normal file
@@ -0,0 +1,582 @@
|
||||
/*-------------------------------------------------------------------------
|
||||
*
|
||||
* prepqual.c--
|
||||
* Routines for preprocessing the parse tree qualification
|
||||
*
|
||||
* Copyright (c) 1994, Regents of the University of California
|
||||
*
|
||||
*
|
||||
* IDENTIFICATION
|
||||
* $Header: /cvsroot/pgsql/src/backend/optimizer/prep/prepqual.c,v 1.1.1.1 1996/07/09 06:21:38 scrappy Exp $
|
||||
*
|
||||
*-------------------------------------------------------------------------
|
||||
*/
|
||||
#include "postgres.h"
|
||||
|
||||
#include "nodes/pg_list.h"
|
||||
#include "nodes/makefuncs.h"
|
||||
|
||||
#include "optimizer/internal.h"
|
||||
#include "optimizer/clauses.h"
|
||||
#include "optimizer/prep.h"
|
||||
|
||||
#include "utils/lsyscache.h"
|
||||
|
||||
static Expr *pull_args(Expr *qual);
|
||||
static List *pull_ors(List *orlist);
|
||||
static List *pull_ands(List *andlist);
|
||||
static Expr *find_nots(Expr *qual);
|
||||
static Expr *push_nots(Expr *qual);
|
||||
static Expr *normalize(Expr *qual);
|
||||
static List *or_normalize(List *orlist);
|
||||
static List *distribute_args(List *item, List *args);
|
||||
static List *qualcleanup(Expr *qual);
|
||||
static List *remove_ands(Expr *qual);
|
||||
static List *remove_duplicates(List *list);
|
||||
|
||||
/*
|
||||
* preprocess-qualification--
|
||||
* Driver routine for modifying the parse tree qualification.
|
||||
*
|
||||
* Returns the new base qualification and the existential qualification
|
||||
* in existentialQualPtr.
|
||||
*
|
||||
* XXX right now, update_clauses() does nothing so
|
||||
* preprocess-qualification simply converts the qual in conjunctive
|
||||
* normal form (see cnfify() below )
|
||||
*/
|
||||
List *
|
||||
preprocess_qualification(Expr *qual, List *tlist, List **existentialQualPtr)
|
||||
{
|
||||
List *cnf_qual = cnfify(qual, true);
|
||||
/*
|
||||
List *existential_qual =
|
||||
update_clauses(intCons(_query_result_relation_,
|
||||
update_relations(tlist)),
|
||||
cnf_qual,
|
||||
_query_command_type_);
|
||||
if (existential_qual) {
|
||||
*existentialQualPtr = existential_qual;
|
||||
return set_difference(cnf_qual, existential_qual);
|
||||
} else {
|
||||
*existentialQualPtr = NIL;
|
||||
return cnf_qual;
|
||||
}
|
||||
*/
|
||||
/* update_clauses() is not working right now */
|
||||
*existentialQualPtr = NIL;
|
||||
return cnf_qual;
|
||||
|
||||
}
|
||||
|
||||
/*****************************************************************************
|
||||
*
|
||||
* CNF CONVERSION ROUTINES
|
||||
*
|
||||
* NOTES:
|
||||
* The basic algorithms for normalizing the qualification are taken
|
||||
* from ingres/source/qrymod/norml.c
|
||||
*
|
||||
* Remember that the initial qualification may consist of ARBITRARY
|
||||
* combinations of clauses. In addition, before this routine is called,
|
||||
* the qualification will contain explicit "AND"s.
|
||||
*
|
||||
*****************************************************************************/
|
||||
|
||||
|
||||
/*
|
||||
* cnfify--
|
||||
* Convert a qualification to conjunctive normal form by applying
|
||||
* successive normalizations.
|
||||
*
|
||||
* Returns the modified qualification with an extra level of nesting.
|
||||
*
|
||||
* If 'removeAndFlag' is true then it removes the explicit ANDs.
|
||||
*
|
||||
* NOTE: this routine is called by the planner (removeAndFlag = true)
|
||||
* and from the rule manager (removeAndFlag = false).
|
||||
*
|
||||
*/
|
||||
List *
|
||||
cnfify(Expr *qual, bool removeAndFlag)
|
||||
{
|
||||
Expr *newqual = NULL;
|
||||
|
||||
if (qual != NULL) {
|
||||
newqual = find_nots(pull_args(qual));
|
||||
newqual = normalize(pull_args(newqual));
|
||||
newqual = (Expr*)qualcleanup(pull_args(newqual));
|
||||
newqual = pull_args(newqual);;
|
||||
|
||||
if (removeAndFlag) {
|
||||
if(and_clause((Node*)newqual))
|
||||
newqual=(Expr*)remove_ands(newqual);
|
||||
else
|
||||
newqual=(Expr*)remove_ands(make_andclause(lcons(newqual,NIL)));
|
||||
}
|
||||
}
|
||||
else if (qual!=NULL)
|
||||
newqual = (Expr*)lcons(qual, NIL);
|
||||
|
||||
return (List*)(newqual);
|
||||
}
|
||||
|
||||
/*
|
||||
* pull-args--
|
||||
* Given a qualification, eliminate nested 'and' and 'or' clauses.
|
||||
*
|
||||
* Returns the modified qualification.
|
||||
*
|
||||
*/
|
||||
static Expr *
|
||||
pull_args(Expr *qual)
|
||||
{
|
||||
if (qual==NULL)
|
||||
return (NULL);
|
||||
|
||||
if (is_opclause((Node*)qual)) {
|
||||
return(make_clause(qual->opType, qual->oper,
|
||||
lcons(pull_args((Expr*)get_leftop(qual)),
|
||||
lcons(pull_args((Expr*)get_rightop(qual)),
|
||||
NIL))));
|
||||
} else if (and_clause((Node*)qual)) {
|
||||
List *temp = NIL;
|
||||
List *t_list = NIL;
|
||||
|
||||
foreach (temp, qual->args)
|
||||
t_list = lappend (t_list, pull_args(lfirst(temp)));
|
||||
return (make_andclause (pull_ands (t_list)));
|
||||
}else if (or_clause((Node*)qual)) {
|
||||
List *temp = NIL;
|
||||
List *t_list = NIL;
|
||||
|
||||
foreach (temp, qual->args)
|
||||
t_list = lappend (t_list, pull_args(lfirst(temp)));
|
||||
return (make_orclause (pull_ors (t_list)));
|
||||
} else if (not_clause((Node*)qual)) {
|
||||
return (make_notclause (pull_args (get_notclausearg (qual))));
|
||||
} else {
|
||||
return (qual);
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* pull-ors--
|
||||
* Pull the arguments of an 'or' clause nested within another 'or'
|
||||
* clause up into the argument list of the parent.
|
||||
*
|
||||
* Returns the modified list.
|
||||
*/
|
||||
static List *
|
||||
pull_ors(List *orlist)
|
||||
{
|
||||
if (orlist==NIL)
|
||||
return (NIL);
|
||||
|
||||
if (or_clause(lfirst(orlist))) {
|
||||
List *args = ((Expr*)lfirst(orlist))->args;
|
||||
return (pull_ors(nconc(copyObject((Node*)args),
|
||||
copyObject((Node*)lnext(orlist)))));
|
||||
} else {
|
||||
return (lcons(lfirst(orlist), pull_ors(lnext(orlist))));
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* pull-ands--
|
||||
* Pull the arguments of an 'and' clause nested within another 'and'
|
||||
* clause up into the argument list of the parent.
|
||||
*
|
||||
* Returns the modified list.
|
||||
*/
|
||||
static List *
|
||||
pull_ands(List *andlist)
|
||||
{
|
||||
if (andlist==NIL)
|
||||
return (NIL);
|
||||
|
||||
if (and_clause (lfirst(andlist))) {
|
||||
List *args = ((Expr*)lfirst(andlist))->args;
|
||||
return (pull_ands(nconc(copyObject((Node*)args),
|
||||
copyObject((Node*)lnext(andlist)))));
|
||||
} else {
|
||||
return (lcons(lfirst(andlist), pull_ands(lnext(andlist))));
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* find-nots--
|
||||
* Traverse the qualification, looking for 'not's to take care of.
|
||||
* For 'not' clauses, remove the 'not' and push it down to the clauses'
|
||||
* descendants.
|
||||
* For all other clause types, simply recurse.
|
||||
*
|
||||
* Returns the modified qualification.
|
||||
*
|
||||
*/
|
||||
static Expr *
|
||||
find_nots(Expr *qual)
|
||||
{
|
||||
if (qual==NULL)
|
||||
return (NULL);
|
||||
|
||||
if (is_opclause((Node*)qual)) {
|
||||
return (make_clause(qual->opType, qual->oper,
|
||||
lcons(find_nots((Expr*)get_leftop(qual)),
|
||||
lcons(find_nots((Expr*)get_rightop(qual)),
|
||||
NIL))));
|
||||
} else if (and_clause ((Node*)qual)) {
|
||||
List *temp = NIL;
|
||||
List *t_list = NIL;
|
||||
|
||||
foreach (temp, qual->args) {
|
||||
t_list = lappend(t_list,find_nots(lfirst(temp)));
|
||||
}
|
||||
|
||||
return (make_andclause(t_list));
|
||||
} else if (or_clause((Node*)qual)) {
|
||||
List *temp = NIL;
|
||||
List *t_list = NIL;
|
||||
|
||||
foreach (temp, qual->args) {
|
||||
t_list = lappend(t_list,find_nots(lfirst(temp)));
|
||||
}
|
||||
return (make_orclause (t_list));
|
||||
} else if (not_clause((Node*)qual))
|
||||
return (push_nots(get_notclausearg (qual)));
|
||||
else
|
||||
return (qual);
|
||||
}
|
||||
|
||||
/*
|
||||
* push-nots--
|
||||
* Negate the descendants of a 'not' clause.
|
||||
*
|
||||
* Returns the modified qualification.
|
||||
*
|
||||
*/
|
||||
static Expr *
|
||||
push_nots(Expr *qual)
|
||||
{
|
||||
if (qual==NULL)
|
||||
return (NULL);
|
||||
|
||||
/*
|
||||
* Negate an operator clause if possible:
|
||||
* ("NOT" (< A B)) => (> A B)
|
||||
* Otherwise, retain the clause as it is (the 'not' can't be pushed
|
||||
* down any farther).
|
||||
*/
|
||||
if (is_opclause((Node*)qual)) {
|
||||
Oper *oper = (Oper*)((Expr*)qual)->oper;
|
||||
Oid negator = get_negator(oper->opno);
|
||||
|
||||
if(negator) {
|
||||
Oper *op = (Oper*) makeOper(negator,
|
||||
InvalidOid,
|
||||
oper->opresulttype,
|
||||
0, NULL);
|
||||
op->op_fcache = (FunctionCache *) NULL;
|
||||
return
|
||||
(make_opclause(op, get_leftop(qual), get_rightop(qual)));
|
||||
} else {
|
||||
return (make_notclause(qual));
|
||||
}
|
||||
} else if (and_clause((Node*)qual)) {
|
||||
/* Apply DeMorgan's Laws:
|
||||
* ("NOT" ("AND" A B)) => ("OR" ("NOT" A) ("NOT" B))
|
||||
* ("NOT" ("OR" A B)) => ("AND" ("NOT" A) ("NOT" B))
|
||||
* i.e., continue negating down through the clause's descendants.
|
||||
*/
|
||||
List *temp = NIL;
|
||||
List *t_list = NIL;
|
||||
|
||||
foreach(temp, qual->args) {
|
||||
t_list = lappend(t_list,push_nots(lfirst(temp)));
|
||||
}
|
||||
return (make_orclause (t_list));
|
||||
} else if (or_clause((Node*)qual)) {
|
||||
List *temp = NIL;
|
||||
List *t_list = NIL;
|
||||
|
||||
foreach(temp, qual->args) {
|
||||
t_list = lappend(t_list,push_nots(lfirst(temp)));
|
||||
}
|
||||
return (make_andclause (t_list));
|
||||
} else if (not_clause((Node*)qual))
|
||||
/* Another 'not' cancels this 'not', so eliminate the 'not' and
|
||||
* stop negating this branch.
|
||||
*/
|
||||
return (find_nots (get_notclausearg (qual)));
|
||||
else
|
||||
/* We don't know how to negate anything else, place a 'not' at this
|
||||
* level.
|
||||
*/
|
||||
return (make_notclause (qual));
|
||||
}
|
||||
|
||||
/*
|
||||
* normalize--
|
||||
* Given a qualification tree with the 'not's pushed down, convert it
|
||||
* to a tree in CNF by repeatedly applying the rule:
|
||||
* ("OR" A ("AND" B C)) => ("AND" ("OR" A B) ("OR" A C))
|
||||
* bottom-up.
|
||||
* Note that 'or' clauses will always be turned into 'and' clauses.
|
||||
*
|
||||
* Returns the modified qualification.
|
||||
*
|
||||
*/
|
||||
static Expr *
|
||||
normalize(Expr *qual)
|
||||
{
|
||||
if (qual==NULL)
|
||||
return (NULL);
|
||||
|
||||
if (is_opclause((Node*)qual)) {
|
||||
Expr *expr = (Expr*)qual;
|
||||
return (make_clause(expr->opType, expr->oper,
|
||||
lcons(normalize((Expr*)get_leftop(qual)),
|
||||
lcons(normalize((Expr*)get_rightop(qual)),
|
||||
NIL))));
|
||||
} else if (and_clause((Node*)qual)) {
|
||||
List *temp = NIL;
|
||||
List *t_list = NIL;
|
||||
|
||||
foreach (temp, qual->args) {
|
||||
t_list = lappend(t_list,normalize(lfirst(temp)));
|
||||
}
|
||||
return (make_andclause (t_list));
|
||||
} else if (or_clause((Node*)qual)) {
|
||||
/* XXX - let form, maybe incorrect */
|
||||
List *orlist = NIL;
|
||||
List *temp = NIL;
|
||||
bool has_andclause = FALSE;
|
||||
|
||||
foreach(temp, qual->args) {
|
||||
orlist = lappend(orlist,normalize(lfirst(temp)));
|
||||
}
|
||||
foreach (temp, orlist) {
|
||||
if (and_clause (lfirst(temp))) {
|
||||
has_andclause = TRUE;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (has_andclause == TRUE)
|
||||
return (make_andclause(or_normalize(orlist)));
|
||||
else
|
||||
return (make_orclause(orlist));
|
||||
|
||||
} else if (not_clause((Node*)qual))
|
||||
return (make_notclause (normalize (get_notclausearg (qual))));
|
||||
else
|
||||
return (qual);
|
||||
}
|
||||
|
||||
/*
|
||||
* or-normalize--
|
||||
* Given a list of exprs which are 'or'ed together, distribute any
|
||||
* 'and' clauses.
|
||||
*
|
||||
* Returns the modified list.
|
||||
*
|
||||
*/
|
||||
static List *
|
||||
or_normalize(List *orlist)
|
||||
{
|
||||
List *distributable = NIL;
|
||||
List *new_orlist = NIL;
|
||||
List *temp = NIL;
|
||||
|
||||
if (orlist==NIL)
|
||||
return NIL;
|
||||
|
||||
foreach(temp, orlist) {
|
||||
if (and_clause(lfirst(temp)))
|
||||
distributable = lfirst(temp);
|
||||
}
|
||||
if (distributable)
|
||||
new_orlist = LispRemove(distributable,orlist);
|
||||
|
||||
if(new_orlist) {
|
||||
return
|
||||
(or_normalize(lcons(distribute_args(lfirst(new_orlist),
|
||||
((Expr*)distributable)->args),
|
||||
lnext(new_orlist))));
|
||||
}else {
|
||||
return (orlist);
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* distribute-args--
|
||||
* Create new 'or' clauses by or'ing 'item' with each element of 'args'.
|
||||
* E.g.: (distribute-args A ("AND" B C)) => ("AND" ("OR" A B) ("OR" A C))
|
||||
*
|
||||
* Returns an 'and' clause.
|
||||
*
|
||||
*/
|
||||
static List *
|
||||
distribute_args(List *item, List *args)
|
||||
{
|
||||
List *or_list = NIL;
|
||||
List *n_list = NIL;
|
||||
List *temp = NIL;
|
||||
List *t_list = NIL;
|
||||
|
||||
if (args==NULL)
|
||||
return (item);
|
||||
|
||||
foreach (temp,args) {
|
||||
n_list = or_normalize(pull_ors(lcons(item,
|
||||
lcons(lfirst(temp),NIL))));
|
||||
or_list = (List*)make_orclause(n_list);
|
||||
t_list = lappend(t_list,or_list);
|
||||
}
|
||||
return ((List*)make_andclause(t_list));
|
||||
}
|
||||
|
||||
/*
|
||||
* qualcleanup--
|
||||
* Fix up a qualification by removing duplicate entries (left over from
|
||||
* normalization), and by removing 'and' and 'or' clauses which have only
|
||||
* one valid expr (e.g., ("AND" A) => A).
|
||||
*
|
||||
* Returns the modified qualfication.
|
||||
*
|
||||
*/
|
||||
static List *
|
||||
qualcleanup(Expr *qual)
|
||||
{
|
||||
if (qual==NULL)
|
||||
return (NIL);
|
||||
|
||||
if (is_opclause((Node*)qual)) {
|
||||
return ((List*)make_clause(qual->opType, qual->oper,
|
||||
lcons(qualcleanup((Expr*)get_leftop(qual)),
|
||||
lcons(qualcleanup((Expr*)get_rightop(qual)),
|
||||
NIL))));
|
||||
} else if (and_clause((Node*)qual)) {
|
||||
List *temp = NIL;
|
||||
List *t_list = NIL;
|
||||
List *new_and_args = NIL;
|
||||
|
||||
foreach(temp, qual->args)
|
||||
t_list = lappend(t_list,qualcleanup(lfirst(temp)));
|
||||
|
||||
new_and_args = remove_duplicates(t_list);
|
||||
|
||||
if(length (new_and_args) > 1)
|
||||
return ((List*)make_andclause(new_and_args));
|
||||
else
|
||||
return (lfirst(new_and_args));
|
||||
}
|
||||
else if (or_clause((Node*)qual)) {
|
||||
List *temp = NIL;
|
||||
List *t_list = NIL;
|
||||
List *new_or_args = NIL;
|
||||
|
||||
foreach (temp, qual->args)
|
||||
t_list = lappend(t_list,qualcleanup(lfirst(temp)));
|
||||
|
||||
new_or_args = remove_duplicates(t_list);
|
||||
|
||||
|
||||
if(length (new_or_args) > 1)
|
||||
return ((List*)make_orclause (new_or_args));
|
||||
else
|
||||
return (lfirst (new_or_args));
|
||||
} else if (not_clause((Node*)qual))
|
||||
return ((List*)make_notclause((Expr*)qualcleanup((Expr*)get_notclausearg(qual))));
|
||||
|
||||
else
|
||||
return ((List*)qual);
|
||||
}
|
||||
|
||||
/*
|
||||
* remove-ands--
|
||||
* Remove the explicit "AND"s from the qualification:
|
||||
* ("AND" A B) => (A B)
|
||||
*
|
||||
* RETURNS : qual
|
||||
* MODIFIES: qual
|
||||
*/
|
||||
static List *
|
||||
remove_ands(Expr *qual)
|
||||
{
|
||||
List *t_list = NIL;
|
||||
|
||||
if (qual==NULL)
|
||||
return (NIL);
|
||||
if (is_opclause((Node*)qual)) {
|
||||
return ((List*)make_clause(qual->opType, qual->oper,
|
||||
lcons(remove_ands((Expr*)get_leftop(qual)),
|
||||
lcons(remove_ands((Expr*)get_rightop(qual)),
|
||||
NIL))));
|
||||
} else if (and_clause((Node*)qual)) {
|
||||
List *temp = NIL;
|
||||
foreach (temp, qual->args)
|
||||
t_list = lappend(t_list,remove_ands(lfirst(temp)));
|
||||
return(t_list);
|
||||
} else if (or_clause((Node*)qual)) {
|
||||
List *temp = NIL;
|
||||
foreach (temp, qual->args)
|
||||
t_list = lappend(t_list,remove_ands(lfirst(temp)));
|
||||
return ((List*)make_orclause((List*)t_list));
|
||||
} else if (not_clause((Node*)qual)) {
|
||||
return ((List*)make_notclause((Expr*)remove_ands((Expr*)get_notclausearg (qual))));
|
||||
} else {
|
||||
return ((List*)qual);
|
||||
}
|
||||
}
|
||||
|
||||
/*****************************************************************************
|
||||
*
|
||||
* EXISTENTIAL QUALIFICATIONS
|
||||
*
|
||||
*****************************************************************************/
|
||||
|
||||
/*
|
||||
* update-relations--
|
||||
* Returns the range table indices (i.e., varnos) for all relations which
|
||||
* are referenced in the target list.
|
||||
*
|
||||
*/
|
||||
#if 0
|
||||
static List *
|
||||
update_relations(List *tlist)
|
||||
{
|
||||
return(NIL);
|
||||
}
|
||||
#endif
|
||||
|
||||
/*****************************************************************************
|
||||
*
|
||||
*
|
||||
*
|
||||
*****************************************************************************/
|
||||
|
||||
static List *
|
||||
remove_duplicates(List *list)
|
||||
{
|
||||
List *i;
|
||||
List *j;
|
||||
List *result = NIL;
|
||||
bool there_exists_duplicate = false;
|
||||
|
||||
if (length(list) == 1)
|
||||
return(list);
|
||||
|
||||
foreach (i, list) {
|
||||
if (i != NIL) {
|
||||
foreach (j, lnext(i)) {
|
||||
if (equal(lfirst(i), lfirst(j)))
|
||||
there_exists_duplicate = true;
|
||||
}
|
||||
if (!there_exists_duplicate)
|
||||
result = lappend(result, lfirst(i));
|
||||
|
||||
there_exists_duplicate = false;
|
||||
}
|
||||
}
|
||||
return(result);
|
||||
}
|
322
src/backend/optimizer/prep/preptlist.c
Normal file
322
src/backend/optimizer/prep/preptlist.c
Normal file
@@ -0,0 +1,322 @@
|
||||
/*-------------------------------------------------------------------------
|
||||
*
|
||||
* preptlist.c--
|
||||
* Routines to preprocess the parse tree target list
|
||||
*
|
||||
* Copyright (c) 1994, Regents of the University of California
|
||||
*
|
||||
*
|
||||
* IDENTIFICATION
|
||||
* $Header: /cvsroot/pgsql/src/backend/optimizer/prep/preptlist.c,v 1.1.1.1 1996/07/09 06:21:38 scrappy Exp $
|
||||
*
|
||||
*-------------------------------------------------------------------------
|
||||
*/
|
||||
#include <string.h>
|
||||
#include "postgres.h"
|
||||
|
||||
#include "nodes/pg_list.h"
|
||||
#include "nodes/relation.h"
|
||||
#include "nodes/primnodes.h"
|
||||
#include "nodes/parsenodes.h"
|
||||
|
||||
#include "nodes/makefuncs.h"
|
||||
|
||||
#include "utils/builtins.h"
|
||||
#include "utils/lsyscache.h"
|
||||
#include "utils/palloc.h"
|
||||
|
||||
#include "parser/parsetree.h" /* for getrelid() */
|
||||
#include "parser/catalog_utils.h"
|
||||
|
||||
#include "optimizer/internal.h"
|
||||
#include "optimizer/prep.h"
|
||||
#include "optimizer/clauses.h"
|
||||
#include "optimizer/tlist.h"
|
||||
|
||||
static List *expand_targetlist(List *tlist, Oid relid, int command_type,
|
||||
Index result_relation);
|
||||
static List *replace_matching_resname(List *new_tlist,
|
||||
List *old_tlist);
|
||||
static List *new_relation_targetlist(Oid relid, Index rt_index,
|
||||
NodeTag node_type);
|
||||
|
||||
|
||||
/*
|
||||
* preprocess-targetlist--
|
||||
* Driver for preprocessing the parse tree targetlist.
|
||||
*
|
||||
* 1. Deal with appends and replaces by filling missing attributes
|
||||
* in the target list.
|
||||
* 2. Reset operator OIDs to the appropriate regproc ids.
|
||||
*
|
||||
* Returns the new targetlist.
|
||||
*/
|
||||
List *
|
||||
preprocess_targetlist(List *tlist,
|
||||
int command_type,
|
||||
Index result_relation,
|
||||
List *range_table)
|
||||
{
|
||||
List *expanded_tlist = NIL;
|
||||
Oid relid = InvalidOid;
|
||||
List *t_list = NIL;
|
||||
List *temp = NIL;
|
||||
|
||||
if (result_relation>=1 && command_type != CMD_SELECT) {
|
||||
relid = getrelid(result_relation, range_table);
|
||||
}
|
||||
|
||||
/*
|
||||
* for heap_formtuple to work, the targetlist must match the exact
|
||||
* order of the attributes. We also need to fill in the missing
|
||||
* attributes here. -ay 10/94
|
||||
*/
|
||||
expanded_tlist =
|
||||
expand_targetlist(tlist, relid, command_type, result_relation);
|
||||
|
||||
/* XXX should the fix-opids be this early?? */
|
||||
/* was mapCAR */
|
||||
foreach (temp,expanded_tlist) {
|
||||
TargetEntry *tle = lfirst(temp);
|
||||
if (tle->expr)
|
||||
fix_opid(tle->expr);
|
||||
}
|
||||
t_list = copyObject(expanded_tlist);
|
||||
|
||||
/* ------------------
|
||||
* for "replace" or "delete" queries, add ctid of the result
|
||||
* relation into the target list so that the ctid can get
|
||||
* propogate through the execution and in the end ExecReplace()
|
||||
* will find the right tuple to replace or delete. This
|
||||
* extra field will be removed in ExecReplace().
|
||||
* For convinient, we append this extra field to the end of
|
||||
* the target list.
|
||||
* ------------------
|
||||
*/
|
||||
if (command_type == CMD_UPDATE || command_type == CMD_DELETE) {
|
||||
TargetEntry *ctid;
|
||||
Resdom *resdom;
|
||||
Var *var;
|
||||
|
||||
resdom = makeResdom(length(t_list) + 1,
|
||||
27,
|
||||
6,
|
||||
"ctid",
|
||||
0,
|
||||
0,
|
||||
1);
|
||||
|
||||
var = makeVar(result_relation, -1, 27, result_relation, -1);
|
||||
|
||||
ctid = makeNode(TargetEntry);
|
||||
ctid->resdom = resdom;
|
||||
ctid->expr = (Node *)var;
|
||||
t_list = lappend(t_list, ctid);
|
||||
}
|
||||
|
||||
return(t_list);
|
||||
}
|
||||
|
||||
/*****************************************************************************
|
||||
*
|
||||
* TARGETLIST EXPANSION
|
||||
*
|
||||
*****************************************************************************/
|
||||
|
||||
/*
|
||||
* expand-targetlist--
|
||||
* Given a target list as generated by the parser and a result relation,
|
||||
* add targetlist entries for the attributes which have not been used.
|
||||
*
|
||||
* XXX This code is only supposed to work with unnested relations.
|
||||
*
|
||||
* 'tlist' is the original target list
|
||||
* 'relid' is the relid of the result relation
|
||||
* 'command' is the update command
|
||||
*
|
||||
* Returns the expanded target list, sorted in resno order.
|
||||
*/
|
||||
static List *
|
||||
expand_targetlist(List *tlist,
|
||||
Oid relid,
|
||||
int command_type,
|
||||
Index result_relation)
|
||||
{
|
||||
NodeTag node_type = T_Invalid;
|
||||
|
||||
switch (command_type) {
|
||||
case CMD_INSERT:
|
||||
node_type = (NodeTag)T_Const;
|
||||
break;
|
||||
case CMD_UPDATE:
|
||||
node_type = (NodeTag)T_Var;
|
||||
break;
|
||||
}
|
||||
|
||||
if(node_type != T_Invalid) {
|
||||
List *ntlist = new_relation_targetlist(relid,
|
||||
result_relation,
|
||||
node_type);
|
||||
|
||||
return (replace_matching_resname(ntlist, tlist));
|
||||
} else {
|
||||
return (tlist);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
static List *
|
||||
replace_matching_resname(List *new_tlist, List *old_tlist)
|
||||
{
|
||||
List *temp, *i;
|
||||
List *t_list = NIL;
|
||||
|
||||
foreach (i,new_tlist) {
|
||||
TargetEntry *new_tle = (TargetEntry *)lfirst(i);
|
||||
TargetEntry *matching_old_tl = NULL;
|
||||
|
||||
foreach (temp, old_tlist) {
|
||||
TargetEntry *old_tle = (TargetEntry *)lfirst(temp);
|
||||
|
||||
old_tle = lfirst(temp);
|
||||
if (!strcmp(old_tle->resdom->resname,
|
||||
new_tle->resdom->resname)) {
|
||||
matching_old_tl = old_tle;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if(matching_old_tl) {
|
||||
matching_old_tl->resdom->resno =
|
||||
new_tle->resdom->resno;
|
||||
t_list = lappend(t_list, matching_old_tl);
|
||||
}
|
||||
else {
|
||||
t_list = lappend(t_list, new_tle);
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* It is possible that 'old_tlist' has some negative
|
||||
* attributes (i.e. negative resnos). This only happens
|
||||
* if this is a replace/append command and we explicitly
|
||||
* specify a system attribute. Of course this is not a very good
|
||||
* idea if this is a user query, but on the other hand the rule
|
||||
* manager uses this mechanism to replace rule locks.
|
||||
*
|
||||
* So, copy all these entries to the end of the target list
|
||||
* and set their 'resjunk' value to 1 to show that these are
|
||||
* special attributes and have to be treated specially by the
|
||||
* executor!
|
||||
*/
|
||||
foreach (temp, old_tlist) {
|
||||
TargetEntry *old_tle, *new_tl;
|
||||
Resdom *newresno;
|
||||
|
||||
old_tle = lfirst(temp);
|
||||
if (old_tle->resdom->resno < 0) {
|
||||
newresno = (Resdom*) copyObject((Node*)old_tle->resdom);
|
||||
newresno->resno = length(t_list) +1;
|
||||
newresno->resjunk = 1;
|
||||
new_tl = MakeTLE(newresno, old_tle->expr);
|
||||
t_list = lappend(t_list, new_tl);
|
||||
}
|
||||
}
|
||||
|
||||
return (t_list);
|
||||
}
|
||||
|
||||
/*
|
||||
* new-relation-targetlist--
|
||||
* Generate a targetlist for the relation with relation OID 'relid'
|
||||
* and rangetable index 'rt-index'.
|
||||
*
|
||||
* Returns the new targetlist.
|
||||
*/
|
||||
static List *
|
||||
new_relation_targetlist(Oid relid, Index rt_index, NodeTag node_type)
|
||||
{
|
||||
AttrNumber attno;
|
||||
List *t_list = NIL;
|
||||
char *attname;
|
||||
Oid atttype = 0;
|
||||
int16 typlen = 0;
|
||||
bool attisset = false;
|
||||
/* Oid type_id; */
|
||||
/* type_id = RelationIdGetTypeId(relid); */
|
||||
|
||||
for(attno=1; attno <= get_relnatts(relid); attno++) {
|
||||
attname = get_attname(/*type_id,*/ relid, attno);
|
||||
atttype = get_atttype(/*type_id,*/ relid, attno);
|
||||
/*
|
||||
* Since this is an append or replace, the size of any set
|
||||
* attribute is the size of the OID used to represent it.
|
||||
*/
|
||||
attisset = get_attisset(/* type_id,*/ relid, attname);
|
||||
if (attisset) {
|
||||
typlen = tlen(type("oid"));
|
||||
} else {
|
||||
typlen = get_typlen(atttype);
|
||||
}
|
||||
|
||||
switch (node_type) {
|
||||
case T_Const:
|
||||
{
|
||||
struct varlena *typedefault = get_typdefault(atttype);
|
||||
int temp = 0;
|
||||
Const *temp2 = (Const*)NULL;
|
||||
TargetEntry *temp3 = (TargetEntry *)NULL;
|
||||
|
||||
if (typedefault==NULL)
|
||||
temp = 0;
|
||||
else
|
||||
temp = typlen;
|
||||
|
||||
temp2 = makeConst (atttype,
|
||||
temp,
|
||||
(Datum)typedefault,
|
||||
(typedefault == (struct varlena *)NULL),
|
||||
/* XXX this is bullshit */
|
||||
false,
|
||||
false /* not a set */);
|
||||
|
||||
temp3 = MakeTLE (makeResdom(attno,
|
||||
atttype,
|
||||
typlen,
|
||||
attname,
|
||||
0,
|
||||
(Oid)0,
|
||||
0),
|
||||
(Node*)temp2);
|
||||
t_list = lappend(t_list,temp3);
|
||||
break;
|
||||
}
|
||||
case T_Var:
|
||||
{
|
||||
Var *temp_var = (Var*)NULL;
|
||||
TargetEntry *temp_list = NULL;
|
||||
|
||||
temp_var =
|
||||
makeVar(rt_index, attno, atttype, rt_index, attno);
|
||||
|
||||
temp_list = MakeTLE(makeResdom(attno,
|
||||
atttype,
|
||||
typlen,
|
||||
attname,
|
||||
0,
|
||||
(Oid)0,
|
||||
0),
|
||||
(Node*)temp_var);
|
||||
t_list = lappend(t_list,temp_list);
|
||||
break;
|
||||
}
|
||||
default: /* do nothing */
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
return(t_list);
|
||||
}
|
||||
|
||||
|
400
src/backend/optimizer/prep/prepunion.c
Normal file
400
src/backend/optimizer/prep/prepunion.c
Normal file
@@ -0,0 +1,400 @@
|
||||
/*-------------------------------------------------------------------------
|
||||
*
|
||||
* prepunion.c--
|
||||
* Routines to plan archive, inheritance, union, and version queries
|
||||
*
|
||||
* Copyright (c) 1994, Regents of the University of California
|
||||
*
|
||||
*
|
||||
* IDENTIFICATION
|
||||
* $Header: /cvsroot/pgsql/src/backend/optimizer/prep/prepunion.c,v 1.1.1.1 1996/07/09 06:21:38 scrappy Exp $
|
||||
*
|
||||
*-------------------------------------------------------------------------
|
||||
*/
|
||||
#include "postgres.h"
|
||||
|
||||
#include "nodes/nodes.h"
|
||||
#include "nodes/pg_list.h"
|
||||
#include "nodes/execnodes.h"
|
||||
#include "nodes/plannodes.h"
|
||||
#include "nodes/relation.h"
|
||||
|
||||
#include "parser/parse_query.h"
|
||||
#include "parser/parsetree.h"
|
||||
|
||||
#include "utils/elog.h"
|
||||
#include "utils/lsyscache.h"
|
||||
|
||||
#include "optimizer/internal.h"
|
||||
#include "optimizer/prep.h"
|
||||
#include "optimizer/plancat.h"
|
||||
#include "optimizer/planner.h"
|
||||
#include "optimizer/prep.h"
|
||||
|
||||
static List *plan_union_query(List *relids, Index rt_index,
|
||||
RangeTblEntry *rt_entry, Query *parse, UnionFlag flag,
|
||||
List **union_rtentriesPtr);
|
||||
static RangeTblEntry *new_rangetable_entry(Oid new_relid,
|
||||
RangeTblEntry *old_entry);
|
||||
static Query *subst_rangetable(Query *root, Index index,
|
||||
RangeTblEntry *new_entry);
|
||||
static void fix_parsetree_attnums(Index rt_index, Oid old_relid,
|
||||
Oid new_relid, Query *parsetree);
|
||||
static Append *make_append(List *unionplans, Index rt_index,
|
||||
List *union_rt_entries, List *tlist);
|
||||
|
||||
|
||||
/*
|
||||
* find-all-inheritors -
|
||||
* Returns a list of relids corresponding to relations that inherit
|
||||
* attributes from any relations listed in either of the argument relid
|
||||
* lists.
|
||||
*/
|
||||
List *
|
||||
find_all_inheritors(List *unexamined_relids,
|
||||
List *examined_relids)
|
||||
{
|
||||
List *new_inheritors = NIL;
|
||||
List *new_examined_relids = NIL;
|
||||
List *new_unexamined_relids = NIL;
|
||||
|
||||
/* Find all relations which inherit from members of
|
||||
* 'unexamined-relids' and store them in 'new-inheritors'.
|
||||
*/
|
||||
List *rels = NIL;
|
||||
List *newrels = NIL;
|
||||
|
||||
foreach(rels,unexamined_relids) {
|
||||
newrels = (List*)LispUnioni(find_inheritance_children(lfirsti(rels)),
|
||||
newrels);
|
||||
}
|
||||
new_inheritors = newrels;
|
||||
|
||||
new_examined_relids = (List*)LispUnioni(examined_relids,unexamined_relids);
|
||||
new_unexamined_relids = set_differencei(new_inheritors,
|
||||
new_examined_relids);
|
||||
|
||||
if (new_unexamined_relids==NULL) {
|
||||
return(new_examined_relids);
|
||||
} else {
|
||||
return (find_all_inheritors (new_unexamined_relids,
|
||||
new_examined_relids));
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* first-matching-rt-entry -
|
||||
* Given a rangetable, find the first rangetable entry that represents
|
||||
* the appropriate special case.
|
||||
*
|
||||
* Returns a rangetable index., Returns -1 if no matches
|
||||
*/
|
||||
int
|
||||
first_matching_rt_entry (List *rangetable, UnionFlag flag)
|
||||
{
|
||||
int count = 0;
|
||||
List *temp = NIL;
|
||||
|
||||
foreach(temp, rangetable) {
|
||||
RangeTblEntry *rt_entry = lfirst(temp);
|
||||
|
||||
switch(flag) {
|
||||
case INHERITS_FLAG:
|
||||
if (rt_entry->inh)
|
||||
return count+1;
|
||||
break;
|
||||
case ARCHIVE_FLAG:
|
||||
if (rt_entry->archive)
|
||||
return count+1;
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
count++;
|
||||
}
|
||||
|
||||
return(-1);
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* plan-union-queries--
|
||||
*
|
||||
* Plans the queries for a given parent relation.
|
||||
*
|
||||
* Returns a list containing a list of plans and a list of rangetable
|
||||
* entries to be inserted into an APPEND node.
|
||||
* XXX - what exactly does this mean, look for make_append
|
||||
*/
|
||||
Append *
|
||||
plan_union_queries(Index rt_index,
|
||||
Query *parse,
|
||||
UnionFlag flag)
|
||||
{
|
||||
List *rangetable = parse->rtable;
|
||||
RangeTblEntry *rt_entry = rt_fetch(rt_index,rangetable);
|
||||
List *union_relids = NIL;
|
||||
List *union_plans = NIL;
|
||||
List *union_rt_entries = NIL;
|
||||
|
||||
switch (flag) {
|
||||
case INHERITS_FLAG:
|
||||
union_relids =
|
||||
find_all_inheritors(lconsi(rt_entry->relid,
|
||||
NIL),
|
||||
NIL);
|
||||
break;
|
||||
|
||||
#if 0
|
||||
case UNION_FLAG:
|
||||
{
|
||||
Index rt_index = 0;
|
||||
union_plans = handleunion(root,rangetable,tlist,qual);
|
||||
return (make_append (union_plans,
|
||||
rt_index, rangetable,
|
||||
((Plan*)lfirst(union_plans))->targetlist ));
|
||||
}
|
||||
break;
|
||||
#endif
|
||||
|
||||
case VERSION_FLAG:
|
||||
union_relids = VersionGetParents(rt_entry->relid);
|
||||
break;
|
||||
|
||||
case ARCHIVE_FLAG:
|
||||
union_relids = find_archive_rels(rt_entry->relid);
|
||||
break;
|
||||
|
||||
default:
|
||||
/* do nothing */
|
||||
break;
|
||||
}
|
||||
|
||||
/*
|
||||
* Remove the flag for this relation, since we're about to handle it
|
||||
* (do it before recursing!).
|
||||
* XXX destructive parse tree change
|
||||
*/
|
||||
switch(flag) {
|
||||
case INHERITS_FLAG:
|
||||
rt_fetch(rt_index,rangetable)->inh = false;
|
||||
break;
|
||||
case ARCHIVE_FLAG:
|
||||
rt_fetch(rt_index,rangetable)->archive = false;
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
/* XXX - can't find any reason to sort union-relids
|
||||
* as paul did, so we're leaving it out for now
|
||||
* (maybe forever) - jeff & lp
|
||||
*
|
||||
* [maybe so. btw, jeff & lp did the lisp conversion, according to Paul.
|
||||
* -- ay 10/94.]
|
||||
*/
|
||||
union_plans = plan_union_query(union_relids, rt_index, rt_entry,
|
||||
parse, flag, &union_rt_entries);
|
||||
|
||||
return (make_append(union_plans,
|
||||
rt_index,
|
||||
union_rt_entries,
|
||||
((Plan*)lfirst(union_plans))->targetlist));
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* plan-union-query--
|
||||
* Returns a list of plans for 'relids' and a list of range table entries
|
||||
* in union_rtentries.
|
||||
*/
|
||||
static List *
|
||||
plan_union_query(List *relids,
|
||||
Index rt_index,
|
||||
RangeTblEntry *rt_entry,
|
||||
Query *root,
|
||||
UnionFlag flag,
|
||||
List **union_rtentriesPtr)
|
||||
{
|
||||
List *i;
|
||||
List *union_plans = NIL;
|
||||
List *union_rtentries = NIL;
|
||||
|
||||
foreach (i, relids) {
|
||||
int relid = lfirsti(i);
|
||||
RangeTblEntry *new_rt_entry = new_rangetable_entry(relid,
|
||||
rt_entry);
|
||||
Query *new_root = subst_rangetable(root,
|
||||
rt_index,
|
||||
new_rt_entry);
|
||||
|
||||
/* reset the uniqueflag and sortclause in parse tree root, so that
|
||||
* sorting will only be done once after append
|
||||
*/
|
||||
/* new_root->uniqueFlag = false; */
|
||||
new_root->uniqueFlag = NULL;
|
||||
new_root->sortClause = NULL;
|
||||
if (flag == ARCHIVE_FLAG) {
|
||||
/*
|
||||
* the entire union query uses the same (most recent) schema.
|
||||
* to do otherwise would require either ragged tuples or careful
|
||||
* archiving and interpretation of pg_attribute...
|
||||
*/
|
||||
} else {
|
||||
fix_parsetree_attnums(rt_index,
|
||||
rt_entry->relid,
|
||||
relid,
|
||||
new_root);
|
||||
}
|
||||
|
||||
union_plans = lappend(union_plans, planner(new_root));
|
||||
union_rtentries = lappend(union_rtentries, new_rt_entry);
|
||||
}
|
||||
|
||||
*union_rtentriesPtr = union_rtentries;
|
||||
return(union_plans);
|
||||
}
|
||||
|
||||
/*
|
||||
* new-rangetable-entry -
|
||||
* Replaces the name and relid of 'old-entry' with the values for
|
||||
* 'new-relid'.
|
||||
*
|
||||
* Returns a copy of 'old-entry' with the parameters substituted.
|
||||
*/
|
||||
static RangeTblEntry *
|
||||
new_rangetable_entry(Oid new_relid, RangeTblEntry *old_entry)
|
||||
{
|
||||
RangeTblEntry *new_entry = copyObject(old_entry);
|
||||
|
||||
/* ??? someone tell me what the following is doing! - ay 11/94 */
|
||||
if (!strcmp(new_entry->refname, "*CURRENT*") ||
|
||||
!strcmp(new_entry->refname, "*NEW*"))
|
||||
new_entry->refname = get_rel_name(new_relid);
|
||||
else
|
||||
new_entry->relname = get_rel_name(new_relid);
|
||||
|
||||
new_entry->relid = new_relid;
|
||||
return(new_entry);
|
||||
}
|
||||
|
||||
/*
|
||||
* subst-rangetable--
|
||||
* Replaces the 'index'th rangetable entry in 'root' with 'new-entry'.
|
||||
*
|
||||
* Returns a new copy of 'root'.
|
||||
*/
|
||||
static Query *
|
||||
subst_rangetable(Query *root, Index index, RangeTblEntry *new_entry)
|
||||
{
|
||||
Query *new_root = copyObject(root);
|
||||
List *temp = NIL;
|
||||
int i = 0;
|
||||
|
||||
for(temp = new_root->rtable,i =1; i < index; temp =lnext(temp),i++)
|
||||
;
|
||||
lfirst(temp) = new_entry;
|
||||
|
||||
return (new_root);
|
||||
}
|
||||
|
||||
static void
|
||||
fix_parsetree_attnums_nodes(Index rt_index,
|
||||
Oid old_relid,
|
||||
Oid new_relid,
|
||||
Node *node)
|
||||
{
|
||||
if (node==NULL)
|
||||
return;
|
||||
|
||||
switch(nodeTag(node)) {
|
||||
case T_TargetEntry:
|
||||
{
|
||||
TargetEntry *tle = (TargetEntry *)node;
|
||||
|
||||
fix_parsetree_attnums_nodes(rt_index, old_relid, new_relid,
|
||||
tle->expr);
|
||||
}
|
||||
break;
|
||||
case T_Expr:
|
||||
{
|
||||
Expr *expr = (Expr *)node;
|
||||
fix_parsetree_attnums_nodes(rt_index, old_relid, new_relid,
|
||||
(Node*)expr->args);
|
||||
}
|
||||
break;
|
||||
case T_Var:
|
||||
{
|
||||
Var *var = (Var *)node;
|
||||
Oid old_typeid, new_typeid;
|
||||
|
||||
/* old_typeid = RelationIdGetTypeId(old_relid);*/
|
||||
/* new_typeid = RelationIdGetTypeId(new_relid);*/
|
||||
old_typeid = old_relid;
|
||||
new_typeid = new_relid;
|
||||
|
||||
if (var->varno == rt_index && var->varattno != 0) {
|
||||
var->varattno =
|
||||
get_attnum(new_typeid,
|
||||
get_attname(old_typeid, var->varattno));
|
||||
}
|
||||
}
|
||||
break;
|
||||
case T_List:
|
||||
{
|
||||
List *l;
|
||||
foreach(l, (List*)node) {
|
||||
fix_parsetree_attnums_nodes(rt_index, old_relid, new_relid,
|
||||
(Node*)lfirst(l));
|
||||
}
|
||||
}
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* fix-parsetree-attnums--
|
||||
* Replaces attribute numbers from the relation represented by
|
||||
* 'old-relid' in 'parsetree' with the attribute numbers from
|
||||
* 'new-relid'.
|
||||
*
|
||||
* Returns the destructively-modified parsetree.
|
||||
*
|
||||
*/
|
||||
static void
|
||||
fix_parsetree_attnums(Index rt_index,
|
||||
Oid old_relid,
|
||||
Oid new_relid,
|
||||
Query *parsetree)
|
||||
{
|
||||
if (old_relid == new_relid)
|
||||
return;
|
||||
|
||||
fix_parsetree_attnums_nodes(rt_index, old_relid, new_relid,
|
||||
(Node*)parsetree->targetList);
|
||||
fix_parsetree_attnums_nodes(rt_index, old_relid, new_relid,
|
||||
parsetree->qual);
|
||||
}
|
||||
|
||||
static Append *
|
||||
make_append(List *unionplans,
|
||||
Index rt_index,
|
||||
List *union_rt_entries,
|
||||
List *tlist)
|
||||
{
|
||||
Append *node = makeNode(Append);
|
||||
|
||||
node->unionplans = unionplans;
|
||||
node->unionrelid = rt_index;
|
||||
node->unionrtentries = union_rt_entries;
|
||||
node->plan.cost = 0.0;
|
||||
node->plan.state = (EState*)NULL;
|
||||
node->plan.targetlist = tlist;
|
||||
node->plan.qual = NIL;
|
||||
node->plan.lefttree = (Plan*)NULL;
|
||||
node->plan.righttree = (Plan*)NULL;
|
||||
|
||||
return(node);
|
||||
}
|
36
src/backend/optimizer/tlist.h
Normal file
36
src/backend/optimizer/tlist.h
Normal file
@@ -0,0 +1,36 @@
|
||||
/*-------------------------------------------------------------------------
|
||||
*
|
||||
* tlist.h--
|
||||
* prototypes for tlist.c.
|
||||
*
|
||||
*
|
||||
* Copyright (c) 1994, Regents of the University of California
|
||||
*
|
||||
* $Id: tlist.h,v 1.1.1.1 1996/07/09 06:21:34 scrappy Exp $
|
||||
*
|
||||
*-------------------------------------------------------------------------
|
||||
*/
|
||||
#ifndef TLIST_H
|
||||
#define TLIST_H
|
||||
|
||||
extern int exec_tlist_length(List *targelist);
|
||||
extern TargetEntry *tlistentry_member(Var *var, List *targetlist);
|
||||
extern Expr *matching_tlvar(Var *var, List *targetlist);
|
||||
extern void add_tl_element(Rel *rel, Var *var);
|
||||
extern TargetEntry *create_tl_element(Var *var, int resdomno);
|
||||
extern List *get_actual_tlist(List *tlist);
|
||||
extern Resdom *tlist_member(Var *var, List *tlist);
|
||||
extern Resdom *tlist_resdom(List *tlist, Resdom *resnode);
|
||||
|
||||
extern TargetEntry *MakeTLE(Resdom *resdom, Node *expr);
|
||||
extern Var *get_expr(TargetEntry *tle);
|
||||
|
||||
extern TargetEntry *match_varid(Var *test_var, List *tlist);
|
||||
extern List *new_unsorted_tlist(List *targetlist);
|
||||
extern List *copy_vars(List *target, List *source);
|
||||
extern List *flatten_tlist(List *tlist);
|
||||
extern List *flatten_tlist_vars(List *full_tlist,
|
||||
List *flat_tlist);
|
||||
extern void AddGroupAttrToTlist(List *tlist, List *grpCl);
|
||||
|
||||
#endif /* TLIST_H */
|
15
src/backend/optimizer/util/Makefile.inc
Normal file
15
src/backend/optimizer/util/Makefile.inc
Normal file
@@ -0,0 +1,15 @@
|
||||
#-------------------------------------------------------------------------
|
||||
#
|
||||
# Makefile.inc--
|
||||
# Makefile for optimizer/util
|
||||
#
|
||||
# Copyright (c) 1994, Regents of the University of California
|
||||
#
|
||||
#
|
||||
# IDENTIFICATION
|
||||
# $Header: /cvsroot/pgsql/src/backend/optimizer/util/Attic/Makefile.inc,v 1.1.1.1 1996/07/09 06:21:38 scrappy Exp $
|
||||
#
|
||||
#-------------------------------------------------------------------------
|
||||
|
||||
SUBSRCS+= clauseinfo.c clauses.c indexnode.c internal.c plancat.c \
|
||||
joininfo.c keys.c ordering.c pathnode.c relnode.c tlist.c var.c
|
187
src/backend/optimizer/util/clauseinfo.c
Normal file
187
src/backend/optimizer/util/clauseinfo.c
Normal file
@@ -0,0 +1,187 @@
|
||||
/*-------------------------------------------------------------------------
|
||||
*
|
||||
* clauseinfo.c--
|
||||
* ClauseInfo node manipulation routines.
|
||||
*
|
||||
* Copyright (c) 1994, Regents of the University of California
|
||||
*
|
||||
*
|
||||
* IDENTIFICATION
|
||||
* $Header: /cvsroot/pgsql/src/backend/optimizer/util/Attic/clauseinfo.c,v 1.1.1.1 1996/07/09 06:21:38 scrappy Exp $
|
||||
*
|
||||
*-------------------------------------------------------------------------
|
||||
*/
|
||||
#include "postgres.h"
|
||||
|
||||
#include "nodes/relation.h"
|
||||
#include "nodes/nodeFuncs.h"
|
||||
|
||||
#include "optimizer/internal.h"
|
||||
#include "optimizer/clauses.h"
|
||||
#include "optimizer/clauseinfo.h"
|
||||
|
||||
/*
|
||||
* valid-or-clause--
|
||||
*
|
||||
* Returns t iff the clauseinfo node contains a 'normal' 'or' clause.
|
||||
*
|
||||
*/
|
||||
bool
|
||||
valid_or_clause(CInfo *clauseinfo)
|
||||
{
|
||||
if (clauseinfo != NULL &&
|
||||
!single_node((Node*)clauseinfo->clause) &&
|
||||
!clauseinfo->notclause &&
|
||||
or_clause((Node*)clauseinfo->clause))
|
||||
return(true);
|
||||
else
|
||||
return(false);
|
||||
}
|
||||
|
||||
/*
|
||||
* get-actual-clauses--
|
||||
*
|
||||
* Returns a list containing the clauses from 'clauseinfo-list'.
|
||||
*
|
||||
*/
|
||||
List *
|
||||
get_actual_clauses(List *clauseinfo_list)
|
||||
{
|
||||
List *temp = NIL;
|
||||
List *result = NIL;
|
||||
CInfo *clause = (CInfo *)NULL;
|
||||
|
||||
foreach(temp,clauseinfo_list) {
|
||||
clause = (CInfo *)lfirst(temp);
|
||||
result = lappend(result,clause->clause);
|
||||
}
|
||||
return(result);
|
||||
}
|
||||
|
||||
/*
|
||||
* XXX NOTE:
|
||||
* The following routines must return their contents in the same order
|
||||
* (e.g., the first clause's info should be first, and so on) or else
|
||||
* get_index_sel() won't work.
|
||||
*
|
||||
*/
|
||||
|
||||
/*
|
||||
* get_relattvals--
|
||||
* For each member of a list of clauseinfo nodes to be used with an
|
||||
* index, create a vectori-long specifying:
|
||||
* the attnos,
|
||||
* the values of the clause constants, and
|
||||
* flags indicating the type and location of the constant within
|
||||
* each clause.
|
||||
* Each clause is of the form (op var some_type_of_constant), thus the
|
||||
* flag indicating whether the constant is on the left or right should
|
||||
* always be *SELEC-CONSTANT-RIGHT*.
|
||||
*
|
||||
* 'clauseinfo-list' is a list of clauseinfo nodes
|
||||
*
|
||||
* Returns a list of vectori-longs.
|
||||
*
|
||||
*/
|
||||
void
|
||||
get_relattvals(List *clauseinfo_list,
|
||||
List **attnos,
|
||||
List **values,
|
||||
List **flags)
|
||||
{
|
||||
List *result1 = NIL;
|
||||
List *result2 = NIL;
|
||||
List *result3 = NIL;
|
||||
CInfo *temp = (CInfo *)NULL;
|
||||
List *i = NIL;
|
||||
|
||||
foreach (i,clauseinfo_list) {
|
||||
int dummy;
|
||||
AttrNumber attno;
|
||||
Datum constval;
|
||||
int flag;
|
||||
|
||||
temp = (CInfo *)lfirst(i);
|
||||
get_relattval((Node*)temp->clause, &dummy, &attno, &constval, &flag);
|
||||
result1 = lappendi(result1, attno);
|
||||
result2 = lappendi(result2, constval);
|
||||
result3 = lappendi(result3, flag);
|
||||
}
|
||||
|
||||
*attnos = result1;
|
||||
*values = result2;
|
||||
*flags = result3;
|
||||
return;
|
||||
}
|
||||
|
||||
/*
|
||||
* get_joinvars --
|
||||
* Given a list of join clauseinfo nodes to be used with the index
|
||||
* of an inner join relation, return three lists consisting of:
|
||||
* the attributes corresponding to the inner join relation
|
||||
* the value of the inner var clause (always "")
|
||||
* whether the attribute appears on the left or right side of
|
||||
* the operator.
|
||||
*
|
||||
* 'relid' is the inner join relation
|
||||
* 'clauseinfo-list' is a list of qualification clauses to be used with
|
||||
* 'rel'
|
||||
*
|
||||
*/
|
||||
void
|
||||
get_joinvars(Oid relid,
|
||||
List *clauseinfo_list,
|
||||
List **attnos,
|
||||
List **values,
|
||||
List **flags)
|
||||
{
|
||||
List *result1 = NIL;
|
||||
List *result2 = NIL;
|
||||
List *result3 = NIL;
|
||||
List *temp;
|
||||
|
||||
foreach(temp, clauseinfo_list) {
|
||||
CInfo *clauseinfo = lfirst(temp);
|
||||
Expr *clause = clauseinfo->clause;
|
||||
|
||||
if( IsA (get_leftop(clause),Var) &&
|
||||
(relid == (get_leftop(clause))->varno)) {
|
||||
|
||||
result1 = lappendi(result1, (get_leftop(clause))->varattno);
|
||||
result2 = lappend(result2, "");
|
||||
result3 = lappendi(result3, _SELEC_CONSTANT_RIGHT_);
|
||||
} else {
|
||||
result1 = lappendi(result1, (get_rightop(clause))->varattno);
|
||||
result2 = lappend(result2, "");
|
||||
result3 = lappendi(result3, _SELEC_CONSTANT_LEFT_);
|
||||
}
|
||||
}
|
||||
*attnos = result1;
|
||||
*values = result2;
|
||||
*flags = result3;
|
||||
return;
|
||||
}
|
||||
|
||||
/*
|
||||
* get_opnos--
|
||||
* Create and return a list containing the clause operators of each member
|
||||
* of a list of clauseinfo nodes to be used with an index.
|
||||
*
|
||||
*/
|
||||
List *
|
||||
get_opnos(List *clauseinfo_list)
|
||||
{
|
||||
CInfo *temp = (CInfo *)NULL;
|
||||
List *result = NIL;
|
||||
List *i = NIL;
|
||||
|
||||
foreach(i,clauseinfo_list) {
|
||||
temp = (CInfo *)lfirst(i);
|
||||
result =
|
||||
lappendi(result,
|
||||
(((Oper*)temp->clause->oper)->opno));
|
||||
}
|
||||
return(result);
|
||||
}
|
||||
|
||||
|
736
src/backend/optimizer/util/clauses.c
Normal file
736
src/backend/optimizer/util/clauses.c
Normal file
@@ -0,0 +1,736 @@
|
||||
/*-------------------------------------------------------------------------
|
||||
*
|
||||
* clauses.c--
|
||||
* routines to manipulate qualification clauses
|
||||
*
|
||||
* Copyright (c) 1994, Regents of the University of California
|
||||
*
|
||||
*
|
||||
* IDENTIFICATION
|
||||
* $Header: /cvsroot/pgsql/src/backend/optimizer/util/clauses.c,v 1.1.1.1 1996/07/09 06:21:38 scrappy Exp $
|
||||
*
|
||||
* HISTORY
|
||||
* AUTHOR DATE MAJOR EVENT
|
||||
* Andrew Yu Nov 3, 1994 clause.c and clauses.c combined
|
||||
*
|
||||
*-------------------------------------------------------------------------
|
||||
*/
|
||||
|
||||
#include "postgres.h"
|
||||
|
||||
#include "nodes/pg_list.h"
|
||||
#include "nodes/primnodes.h"
|
||||
#include "nodes/relation.h"
|
||||
#include "nodes/parsenodes.h"
|
||||
#include "nodes/makefuncs.h"
|
||||
#include "nodes/nodeFuncs.h"
|
||||
|
||||
#include "catalog/pg_aggregate.h"
|
||||
|
||||
#include "utils/elog.h"
|
||||
#include "utils/syscache.h"
|
||||
#include "utils/lsyscache.h"
|
||||
|
||||
#include "optimizer/clauses.h"
|
||||
#include "optimizer/internal.h"
|
||||
#include "optimizer/var.h"
|
||||
|
||||
|
||||
Expr *
|
||||
make_clause(int type, Node *oper, List *args)
|
||||
{
|
||||
if (type == AND_EXPR || type == OR_EXPR || type == NOT_EXPR ||
|
||||
type == OP_EXPR || type == FUNC_EXPR) {
|
||||
Expr *expr = makeNode(Expr);
|
||||
|
||||
/*
|
||||
* assume type checking already done and we don't need the type of
|
||||
* the expr any more.
|
||||
*/
|
||||
expr->typeOid = InvalidOid;
|
||||
expr->opType = type;
|
||||
expr->oper = oper; /* ignored for AND, OR, NOT */
|
||||
expr->args = args;
|
||||
return expr;
|
||||
}else {
|
||||
/* will this ever happen? translated from lispy C code - ay 10/94 */
|
||||
return((Expr*)args);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/*****************************************************************************
|
||||
* OPERATOR clause functions
|
||||
*****************************************************************************/
|
||||
|
||||
|
||||
/*
|
||||
* is_opclause--
|
||||
*
|
||||
* Returns t iff the clause is an operator clause:
|
||||
* (op expr expr) or (op expr).
|
||||
*
|
||||
* [historical note: is_clause has the exact functionality and is used
|
||||
* throughout the code. They're renamed to is_opclause for clarity.
|
||||
* - ay 10/94.]
|
||||
*/
|
||||
bool
|
||||
is_opclause(Node *clause)
|
||||
{
|
||||
return
|
||||
(clause!=NULL &&
|
||||
nodeTag(clause)==T_Expr && ((Expr*)clause)->opType==OP_EXPR);
|
||||
}
|
||||
|
||||
/*
|
||||
* make_opclause--
|
||||
* Creates a clause given its operator left operand and right
|
||||
* operand (if it is non-null).
|
||||
*
|
||||
*/
|
||||
Expr *
|
||||
make_opclause(Oper *op, Var *leftop, Var *rightop)
|
||||
{
|
||||
Expr *expr = makeNode(Expr);
|
||||
|
||||
expr->typeOid = InvalidOid; /* assume type checking done */
|
||||
expr->opType = OP_EXPR;
|
||||
expr->oper = (Node*)op;
|
||||
expr->args = makeList(leftop, rightop, -1);
|
||||
return expr;
|
||||
}
|
||||
|
||||
/*
|
||||
* get_leftop--
|
||||
*
|
||||
* Returns the left operand of a clause of the form (op expr expr)
|
||||
* or (op expr)
|
||||
* NB: it is assumed (for now) that all expr must be Var nodes
|
||||
*/
|
||||
Var *
|
||||
get_leftop(Expr *clause)
|
||||
{
|
||||
if (clause->args!=NULL)
|
||||
return(lfirst(clause->args));
|
||||
else
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/*
|
||||
* get_rightop
|
||||
*
|
||||
* Returns the right operand in a clause of the form (op expr expr).
|
||||
*
|
||||
*/
|
||||
Var *
|
||||
get_rightop(Expr *clause)
|
||||
{
|
||||
if (clause->args!=NULL && lnext(clause->args)!=NULL)
|
||||
return (lfirst(lnext(clause->args)));
|
||||
else
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/*****************************************************************************
|
||||
* AGG clause functions
|
||||
*****************************************************************************/
|
||||
|
||||
bool
|
||||
agg_clause(Node *clause)
|
||||
{
|
||||
return
|
||||
(clause!=NULL && nodeTag(clause)==T_Aggreg);
|
||||
}
|
||||
|
||||
/*****************************************************************************
|
||||
* FUNC clause functions
|
||||
*****************************************************************************/
|
||||
|
||||
/*
|
||||
* is_funcclause--
|
||||
*
|
||||
* Returns t iff the clause is a function clause: (func { expr }).
|
||||
*
|
||||
*/
|
||||
bool
|
||||
is_funcclause(Node *clause)
|
||||
{
|
||||
return
|
||||
(clause!=NULL &&
|
||||
nodeTag(clause)==T_Expr && ((Expr*)clause)->opType==FUNC_EXPR);
|
||||
}
|
||||
|
||||
/*
|
||||
* make_funcclause--
|
||||
*
|
||||
* Creates a function clause given the FUNC node and the functional
|
||||
* arguments.
|
||||
*
|
||||
*/
|
||||
Expr *
|
||||
make_funcclause(Func *func, List *funcargs)
|
||||
{
|
||||
Expr *expr = makeNode(Expr);
|
||||
|
||||
expr->typeOid = InvalidOid; /* assume type checking done */
|
||||
expr->opType = FUNC_EXPR;
|
||||
expr->oper = (Node*)func;
|
||||
expr->args = funcargs;
|
||||
return expr;
|
||||
}
|
||||
|
||||
/*****************************************************************************
|
||||
* OR clause functions
|
||||
*****************************************************************************/
|
||||
|
||||
/*
|
||||
* or_clause--
|
||||
*
|
||||
* Returns t iff the clause is an 'or' clause: (OR { expr }).
|
||||
*
|
||||
*/
|
||||
bool
|
||||
or_clause(Node *clause)
|
||||
{
|
||||
return
|
||||
(clause!=NULL &&
|
||||
nodeTag(clause)==T_Expr && ((Expr*)clause)->opType==OR_EXPR);
|
||||
}
|
||||
|
||||
/*
|
||||
* make_orclause--
|
||||
*
|
||||
* Creates an 'or' clause given a list of its subclauses.
|
||||
*
|
||||
*/
|
||||
Expr *
|
||||
make_orclause(List *orclauses)
|
||||
{
|
||||
Expr *expr = makeNode(Expr);
|
||||
|
||||
expr->typeOid = InvalidOid; /* assume type checking done */
|
||||
expr->opType = OR_EXPR;
|
||||
expr->oper = NULL;
|
||||
expr->args = orclauses;
|
||||
return expr;
|
||||
}
|
||||
|
||||
/*****************************************************************************
|
||||
* NOT clause functions
|
||||
*****************************************************************************/
|
||||
|
||||
/*
|
||||
* not_clause--
|
||||
*
|
||||
* Returns t iff this is a 'not' clause: (NOT expr).
|
||||
*
|
||||
*/
|
||||
bool
|
||||
not_clause(Node *clause)
|
||||
{
|
||||
return
|
||||
(clause!=NULL &&
|
||||
nodeTag(clause)==T_Expr && ((Expr*)clause)->opType == NOT_EXPR);
|
||||
}
|
||||
|
||||
/*
|
||||
* make_notclause--
|
||||
*
|
||||
* Create a 'not' clause given the expression to be negated.
|
||||
*
|
||||
*/
|
||||
Expr *
|
||||
make_notclause(Expr *notclause)
|
||||
{
|
||||
Expr *expr = makeNode(Expr);
|
||||
|
||||
expr->typeOid = InvalidOid; /* assume type checking done */
|
||||
expr->opType = NOT_EXPR;
|
||||
expr->oper = NULL;
|
||||
expr->args = lcons(notclause, NIL);
|
||||
return expr;
|
||||
}
|
||||
|
||||
/*
|
||||
* get_notclausearg--
|
||||
*
|
||||
* Retrieve the clause within a 'not' clause
|
||||
*
|
||||
*/
|
||||
Expr *
|
||||
get_notclausearg(Expr *notclause)
|
||||
{
|
||||
return(lfirst(notclause->args));
|
||||
}
|
||||
|
||||
/*****************************************************************************
|
||||
* AND clause functions
|
||||
*****************************************************************************/
|
||||
|
||||
|
||||
/*
|
||||
* and_clause--
|
||||
*
|
||||
* Returns t iff its argument is an 'and' clause: (AND { expr }).
|
||||
*
|
||||
*/
|
||||
bool
|
||||
and_clause(Node *clause)
|
||||
{
|
||||
return
|
||||
(clause!=NULL &&
|
||||
nodeTag(clause)==T_Expr && ((Expr*)clause)->opType == AND_EXPR);
|
||||
}
|
||||
/*
|
||||
* make_andclause--
|
||||
*
|
||||
* Create an 'and' clause given its arguments in a list.
|
||||
*
|
||||
*/
|
||||
Expr *
|
||||
make_andclause(List *andclauses)
|
||||
{
|
||||
Expr *expr = makeNode(Expr);
|
||||
|
||||
expr->typeOid = InvalidOid; /* assume type checking done */
|
||||
expr->opType = AND_EXPR;
|
||||
expr->oper = NULL;
|
||||
expr->args = andclauses;
|
||||
return expr;
|
||||
}
|
||||
|
||||
/*****************************************************************************
|
||||
* *
|
||||
* *
|
||||
* *
|
||||
*****************************************************************************/
|
||||
|
||||
|
||||
/*
|
||||
* pull-constant-clauses--
|
||||
* Scans through a list of qualifications and find those that
|
||||
* contain no variables.
|
||||
*
|
||||
* Returns a list of the constant clauses in constantQual and the remaining
|
||||
* quals as the return value.
|
||||
*
|
||||
*/
|
||||
List *
|
||||
pull_constant_clauses(List *quals, List **constantQual)
|
||||
{
|
||||
List *q;
|
||||
List *constqual=NIL;
|
||||
List *restqual=NIL;
|
||||
|
||||
foreach(q, quals) {
|
||||
if (!contain_var_clause(lfirst(q))) {
|
||||
constqual = lcons(lfirst(q), constqual);
|
||||
}else {
|
||||
restqual = lcons(lfirst(q), restqual);
|
||||
}
|
||||
}
|
||||
freeList(quals);
|
||||
*constantQual = constqual;
|
||||
return restqual;
|
||||
}
|
||||
|
||||
/*
|
||||
* clause-relids-vars--
|
||||
* Retrieves relids and vars appearing within a clause.
|
||||
* Returns ((relid1 relid2 ... relidn) (var1 var2 ... varm)) where
|
||||
* vars appear in the clause this is done by recursively searching
|
||||
* through the left and right operands of a clause.
|
||||
*
|
||||
* Returns the list of relids and vars.
|
||||
*
|
||||
* XXX take the nreverse's out later
|
||||
*
|
||||
*/
|
||||
void
|
||||
clause_relids_vars(Node *clause, List **relids, List **vars)
|
||||
{
|
||||
List *clvars = pull_var_clause(clause);
|
||||
List *var_list = NIL;
|
||||
List *varno_list = NIL;
|
||||
List *i = NIL;
|
||||
|
||||
foreach (i, clvars) {
|
||||
Var *var = (Var *)lfirst(i);
|
||||
|
||||
if (!intMember(var->varno, varno_list)) {
|
||||
varno_list = lappendi(varno_list, var->varno);
|
||||
var_list = lappend(var_list, var);
|
||||
}
|
||||
}
|
||||
|
||||
*relids = varno_list;
|
||||
*vars = var_list;
|
||||
return;
|
||||
}
|
||||
|
||||
/*
|
||||
* NumRelids--
|
||||
* (formerly clause-relids)
|
||||
*
|
||||
* Returns the number of different relations referenced in 'clause'.
|
||||
*/
|
||||
int
|
||||
NumRelids(Node *clause)
|
||||
{
|
||||
List *vars = pull_var_clause(clause);
|
||||
List *i = NIL;
|
||||
List *var_list = NIL;
|
||||
|
||||
foreach (i, vars) {
|
||||
Var *var = (Var *)lfirst(i);
|
||||
|
||||
if (!intMember(var->varno, var_list)) {
|
||||
var_list = lconsi(var->varno, var_list);
|
||||
}
|
||||
}
|
||||
|
||||
return(length(var_list));
|
||||
}
|
||||
|
||||
/*
|
||||
* contains-not--
|
||||
*
|
||||
* Returns t iff the clause is a 'not' clause or if any of the
|
||||
* subclauses within an 'or' clause contain 'not's.
|
||||
*
|
||||
*/
|
||||
bool
|
||||
contains_not(Node *clause)
|
||||
{
|
||||
if (single_node(clause))
|
||||
return (false);
|
||||
|
||||
if (not_clause(clause))
|
||||
return (true);
|
||||
|
||||
if (or_clause(clause)) {
|
||||
List *a;
|
||||
foreach(a, ((Expr*)clause)->args) {
|
||||
if (contains_not(lfirst(a)))
|
||||
return (true);
|
||||
}
|
||||
}
|
||||
|
||||
return(false);
|
||||
}
|
||||
|
||||
/*
|
||||
* join-clause-p--
|
||||
*
|
||||
* Returns t iff 'clause' is a valid join clause.
|
||||
*
|
||||
*/
|
||||
bool
|
||||
join_clause_p(Node *clause)
|
||||
{
|
||||
Node *leftop, *rightop;
|
||||
|
||||
if (!is_opclause(clause))
|
||||
return false;
|
||||
|
||||
leftop = (Node*)get_leftop((Expr*)clause);
|
||||
rightop = (Node*)get_rightop((Expr*)clause);
|
||||
|
||||
/*
|
||||
* One side of the clause (i.e. left or right operands)
|
||||
* must either be a var node ...
|
||||
*/
|
||||
if (IsA(leftop,Var) || IsA(rightop,Var))
|
||||
return true;
|
||||
|
||||
/*
|
||||
* ... or a func node.
|
||||
*/
|
||||
if (is_funcclause(leftop) || is_funcclause(rightop))
|
||||
return(true);
|
||||
|
||||
return(false);
|
||||
}
|
||||
|
||||
/*
|
||||
* qual-clause-p--
|
||||
*
|
||||
* Returns t iff 'clause' is a valid qualification clause.
|
||||
*
|
||||
*/
|
||||
bool
|
||||
qual_clause_p(Node *clause)
|
||||
{
|
||||
if (!is_opclause(clause))
|
||||
return false;
|
||||
|
||||
if (IsA (get_leftop((Expr*)clause),Var) &&
|
||||
IsA (get_rightop((Expr*)clause),Const))
|
||||
{
|
||||
return(true);
|
||||
}
|
||||
else if (IsA (get_rightop((Expr*)clause),Var) &&
|
||||
IsA (get_leftop((Expr*)clause),Const))
|
||||
{
|
||||
return(true);
|
||||
}
|
||||
return(false);
|
||||
}
|
||||
|
||||
/*
|
||||
* fix-opid--
|
||||
* Calculate the opfid from the opno...
|
||||
*
|
||||
* Returns nothing.
|
||||
*
|
||||
*/
|
||||
void
|
||||
fix_opid(Node *clause)
|
||||
{
|
||||
if (clause==NULL || single_node(clause)) {
|
||||
;
|
||||
}
|
||||
else if (or_clause (clause)) {
|
||||
fix_opids(((Expr*)clause)->args);
|
||||
}
|
||||
else if (is_funcclause (clause)) {
|
||||
fix_opids(((Expr*)clause)->args);
|
||||
}
|
||||
else if (IsA(clause,ArrayRef)) {
|
||||
ArrayRef *aref = (ArrayRef *)clause;
|
||||
|
||||
fix_opids(aref->refupperindexpr);
|
||||
fix_opids(aref->reflowerindexpr);
|
||||
fix_opid(aref->refexpr);
|
||||
fix_opid(aref->refassgnexpr);
|
||||
}
|
||||
else if (not_clause(clause)) {
|
||||
fix_opid((Node*)get_notclausearg((Expr*)clause));
|
||||
}
|
||||
else if (is_opclause (clause)) {
|
||||
replace_opid((Oper*)((Expr*)clause)->oper);
|
||||
fix_opid((Node*)get_leftop((Expr*)clause));
|
||||
fix_opid((Node*)get_rightop((Expr*)clause));
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/*
|
||||
* fix-opids--
|
||||
* Calculate the opfid from the opno for all the clauses...
|
||||
*
|
||||
* Returns its argument.
|
||||
*
|
||||
*/
|
||||
List *
|
||||
fix_opids(List *clauses)
|
||||
{
|
||||
List *clause;
|
||||
|
||||
foreach(clause, clauses)
|
||||
fix_opid(lfirst(clause));
|
||||
|
||||
return(clauses);
|
||||
}
|
||||
|
||||
/*
|
||||
* get_relattval--
|
||||
* For a non-join clause, returns a list consisting of the
|
||||
* relid,
|
||||
* attno,
|
||||
* value of the CONST node (if any), and a
|
||||
* flag indicating whether the value appears on the left or right
|
||||
* of the operator and whether the value varied.
|
||||
*
|
||||
* OLD OBSOLETE COMMENT FOLLOWS:
|
||||
* If 'clause' is not of the format (op var node) or (op node var),
|
||||
* or if the var refers to a nested attribute, then -1's are returned for
|
||||
* everything but the value a blank string "" (pointer to \0) is
|
||||
* returned for the value if it is unknown or null.
|
||||
* END OF OLD OBSOLETE COMMENT.
|
||||
* NEW COMMENT:
|
||||
* when defining rules one of the attibutes of the operator can
|
||||
* be a Param node (which is supposed to be treated as a constant).
|
||||
* However as there is no value specified for a parameter until run time
|
||||
* this routine used to return "" as value, which made 'compute_selec'
|
||||
* to bomb (because it was expecting a lisp integer and got back a lisp
|
||||
* string). Now the code returns a plain old good "lispInteger(0)".
|
||||
*
|
||||
*/
|
||||
void
|
||||
get_relattval(Node *clause,
|
||||
int *relid,
|
||||
AttrNumber *attno,
|
||||
Datum *constval,
|
||||
int *flag)
|
||||
{
|
||||
Var *left = get_leftop((Expr*)clause);
|
||||
Var *right = get_rightop((Expr*)clause);
|
||||
|
||||
if(is_opclause(clause) && IsA(left,Var) &&
|
||||
IsA(right,Const)) {
|
||||
|
||||
if(right!=NULL) {
|
||||
|
||||
*relid = left->varno;
|
||||
*attno = left->varattno;
|
||||
*constval = ((Const *)right)->constvalue;
|
||||
*flag = (_SELEC_CONSTANT_RIGHT_ | _SELEC_IS_CONSTANT_);
|
||||
|
||||
} else {
|
||||
|
||||
*relid = left->varno;
|
||||
*attno = left->varattno;
|
||||
*constval = 0;
|
||||
*flag = (_SELEC_CONSTANT_RIGHT_ | _SELEC_NOT_CONSTANT_);
|
||||
|
||||
}
|
||||
}else if (is_opclause(clause) &&
|
||||
is_funcclause((Node*)left) &&
|
||||
IsA(right,Const)) {
|
||||
List *args = ((Expr*)left)->args;
|
||||
|
||||
|
||||
*relid = ((Var*)lfirst(args))->varno;
|
||||
*attno = InvalidAttrNumber;
|
||||
*constval = ((Const*)right)->constvalue;
|
||||
*flag = (_SELEC_CONSTANT_RIGHT_ | _SELEC_IS_CONSTANT_);
|
||||
|
||||
/*
|
||||
* XXX both of these func clause handling if's seem wrong to me.
|
||||
* they assume that the first argument is the Var. It could
|
||||
* not handle (for example) f(1, emp.name). I think I may have
|
||||
* been assuming no constants in functional index scans when I
|
||||
* implemented this originally (still currently true).
|
||||
* -mer 10 Aug 1992
|
||||
*/
|
||||
} else if (is_opclause(clause) &&
|
||||
is_funcclause((Node*)right) &&
|
||||
IsA(left,Const)) {
|
||||
List *args = ((Expr*)right)->args;
|
||||
|
||||
*relid = ((Var*)lfirst(args))->varno;
|
||||
*attno = InvalidAttrNumber;
|
||||
*constval = ((Const*)left)->constvalue;
|
||||
*flag = ( _SELEC_IS_CONSTANT_);
|
||||
|
||||
} else if (is_opclause (clause) && IsA (right,Var) &&
|
||||
IsA (left,Const)) {
|
||||
if (left!=NULL) {
|
||||
|
||||
*relid = right->varno;
|
||||
*attno = right->varattno;
|
||||
*constval = ((Const*)left)->constvalue;
|
||||
*flag = (_SELEC_IS_CONSTANT_);
|
||||
} else {
|
||||
|
||||
*relid = right->varno;
|
||||
*attno = right->varattno;
|
||||
*constval = 0;
|
||||
*flag = (_SELEC_NOT_CONSTANT_);
|
||||
}
|
||||
} else {
|
||||
/* One or more of the operands are expressions
|
||||
* (e.g., oper clauses)
|
||||
*/
|
||||
*relid = _SELEC_VALUE_UNKNOWN_;
|
||||
*attno = _SELEC_VALUE_UNKNOWN_;
|
||||
*constval = 0;
|
||||
*flag = (_SELEC_NOT_CONSTANT_);
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* get_relsatts--
|
||||
*
|
||||
* Returns a list
|
||||
* ( relid1 attno1 relid2 attno2 )
|
||||
* for a joinclause.
|
||||
*
|
||||
* If the clause is not of the form (op var var) or if any of the vars
|
||||
* refer to nested attributes, then -1's are returned.
|
||||
*
|
||||
*/
|
||||
void
|
||||
get_rels_atts(Node *clause,
|
||||
int *relid1,
|
||||
AttrNumber *attno1,
|
||||
int *relid2,
|
||||
AttrNumber *attno2)
|
||||
{
|
||||
Var *left = get_leftop((Expr*)clause);
|
||||
Var *right = get_rightop((Expr*)clause);
|
||||
bool var_left = (IsA(left,Var));
|
||||
bool var_right = (IsA(right,Var));
|
||||
bool varexpr_left = (bool)((IsA(left,Func) || IsA (left,Oper)) &&
|
||||
contain_var_clause((Node*)left));
|
||||
bool varexpr_right = (bool)(( IsA(right,Func) || IsA (right,Oper)) &&
|
||||
contain_var_clause((Node*)right));
|
||||
|
||||
if (is_opclause(clause)) {
|
||||
if(var_left && var_right) {
|
||||
|
||||
*relid1 = left->varno;
|
||||
*attno1 = left->varoattno;
|
||||
*relid2 = right->varno;
|
||||
*attno2 = right->varoattno;
|
||||
return;
|
||||
} else if (var_left && varexpr_right ) {
|
||||
|
||||
*relid1 = left->varno;
|
||||
*attno1 = left->varoattno;
|
||||
*relid2 = _SELEC_VALUE_UNKNOWN_;
|
||||
*attno2 = _SELEC_VALUE_UNKNOWN_;
|
||||
return;
|
||||
} else if (varexpr_left && var_right) {
|
||||
|
||||
*relid1 = _SELEC_VALUE_UNKNOWN_;
|
||||
*attno1 = _SELEC_VALUE_UNKNOWN_;
|
||||
*relid2 = right->varno;
|
||||
*attno2 = right->varoattno;
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
*relid1 = _SELEC_VALUE_UNKNOWN_;
|
||||
*attno1 = _SELEC_VALUE_UNKNOWN_;
|
||||
*relid2 = _SELEC_VALUE_UNKNOWN_;
|
||||
*attno2 = _SELEC_VALUE_UNKNOWN_;
|
||||
return;
|
||||
}
|
||||
|
||||
void
|
||||
CommuteClause(Node *clause)
|
||||
{
|
||||
Node *temp;
|
||||
Oper *commu;
|
||||
OperatorTupleForm commuTup;
|
||||
HeapTuple heapTup;
|
||||
|
||||
if (!is_opclause(clause))
|
||||
return;
|
||||
|
||||
heapTup = (HeapTuple)
|
||||
get_operator_tuple(get_commutator(((Oper*)((Expr*)clause)->oper)->opno));
|
||||
|
||||
if (heapTup == (HeapTuple)NULL)
|
||||
return;
|
||||
|
||||
commuTup = (OperatorTupleForm)GETSTRUCT(heapTup);
|
||||
|
||||
commu = makeOper(heapTup->t_oid,
|
||||
InvalidOid,
|
||||
commuTup->oprresult,
|
||||
((Oper*)((Expr*)clause)->oper)->opsize,
|
||||
NULL);
|
||||
|
||||
/*
|
||||
* reform the clause -> (operator func/var constant)
|
||||
*/
|
||||
((Expr*)clause)->oper = (Node*)commu;
|
||||
temp = lfirst(((Expr*)clause)->args);
|
||||
lfirst(((Expr*)clause)->args) = lsecond(((Expr*)clause)->args);
|
||||
lsecond(((Expr*)clause)->args) = temp;
|
||||
}
|
||||
|
||||
|
92
src/backend/optimizer/util/indexnode.c
Normal file
92
src/backend/optimizer/util/indexnode.c
Normal file
@@ -0,0 +1,92 @@
|
||||
/*-------------------------------------------------------------------------
|
||||
*
|
||||
* indexnode.c--
|
||||
* Routines to find all indices on a relation
|
||||
*
|
||||
* Copyright (c) 1994, Regents of the University of California
|
||||
*
|
||||
*
|
||||
* IDENTIFICATION
|
||||
* $Header: /cvsroot/pgsql/src/backend/optimizer/util/Attic/indexnode.c,v 1.1.1.1 1996/07/09 06:21:38 scrappy Exp $
|
||||
*
|
||||
*-------------------------------------------------------------------------
|
||||
*/
|
||||
#include "postgres.h"
|
||||
|
||||
#include "nodes/plannodes.h"
|
||||
#include "nodes/parsenodes.h"
|
||||
#include "nodes/relation.h"
|
||||
|
||||
#include "optimizer/internal.h"
|
||||
#include "optimizer/plancat.h"
|
||||
#include "optimizer/pathnode.h" /* where the decls go */
|
||||
|
||||
|
||||
static List *find_secondary_index(Query *root, Oid relid);
|
||||
|
||||
/*
|
||||
* find-relation-indices--
|
||||
* Returns a list of index nodes containing appropriate information for
|
||||
* each (secondary) index defined on a relation.
|
||||
*
|
||||
*/
|
||||
List *
|
||||
find_relation_indices(Query *root, Rel *rel)
|
||||
{
|
||||
if (rel->indexed) {
|
||||
return (find_secondary_index(root, lfirsti(rel->relids)));
|
||||
} else {
|
||||
return (NIL);
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* find-secondary-index--
|
||||
* Creates a list of index path nodes containing information for each
|
||||
* secondary index defined on a relation by searching through the index
|
||||
* catalog.
|
||||
*
|
||||
* 'relid' is the OID of the relation for which indices are being located
|
||||
*
|
||||
* Returns a list of new index nodes.
|
||||
*
|
||||
*/
|
||||
static List *
|
||||
find_secondary_index(Query *root, Oid relid)
|
||||
{
|
||||
IdxInfoRetval indexinfo;
|
||||
List *indexes = NIL;
|
||||
bool first = TRUE;
|
||||
|
||||
while (index_info(root, first, relid,&indexinfo)) {
|
||||
Rel *indexnode = makeNode(Rel);
|
||||
|
||||
indexnode->relids = lconsi(indexinfo.relid,NIL);
|
||||
indexnode->relam = indexinfo.relam;
|
||||
indexnode->pages = indexinfo.pages;
|
||||
indexnode->tuples = indexinfo.tuples;
|
||||
indexnode->indexkeys = indexinfo.indexkeys;
|
||||
indexnode->ordering = indexinfo.orderOprs;
|
||||
indexnode->classlist = indexinfo.classlist;
|
||||
indexnode->indproc= indexinfo.indproc;
|
||||
indexnode->indpred = (List*)indexinfo.indpred;
|
||||
|
||||
indexnode->indexed= false; /* not indexed itself */
|
||||
indexnode->size = 0;
|
||||
indexnode->width= 0;
|
||||
indexnode->targetlist= NIL;
|
||||
indexnode->pathlist= NIL;
|
||||
indexnode->unorderedpath= NULL;
|
||||
indexnode->cheapestpath= NULL;
|
||||
indexnode->pruneable= true;
|
||||
indexnode->clauseinfo= NIL;
|
||||
indexnode->joininfo= NIL;
|
||||
indexnode->innerjoin= NIL;
|
||||
|
||||
indexes = lcons(indexnode, indexes);
|
||||
first = FALSE;
|
||||
}
|
||||
|
||||
return indexes;
|
||||
}
|
||||
|
61
src/backend/optimizer/util/internal.c
Normal file
61
src/backend/optimizer/util/internal.c
Normal file
@@ -0,0 +1,61 @@
|
||||
/*-------------------------------------------------------------------------
|
||||
*
|
||||
* internal.c--
|
||||
* Definitions required throughout the query optimizer.
|
||||
*
|
||||
* Copyright (c) 1994, Regents of the University of California
|
||||
*
|
||||
*
|
||||
* IDENTIFICATION
|
||||
* $Header: /cvsroot/pgsql/src/backend/optimizer/util/Attic/internal.c,v 1.1.1.1 1996/07/09 06:21:38 scrappy Exp $
|
||||
*
|
||||
*-------------------------------------------------------------------------
|
||||
*/
|
||||
|
||||
/*
|
||||
* ---------- SHARED MACROS
|
||||
*
|
||||
* Macros common to modules for creating, accessing, and modifying
|
||||
* query tree and query plan components.
|
||||
* Shared with the executor.
|
||||
*
|
||||
*/
|
||||
|
||||
|
||||
#include "optimizer/internal.h"
|
||||
|
||||
#include "nodes/relation.h"
|
||||
#include "nodes/plannodes.h"
|
||||
#include "nodes/primnodes.h"
|
||||
#include "utils/elog.h"
|
||||
#include "utils/palloc.h"
|
||||
|
||||
#if 0
|
||||
/*****************************************************************************
|
||||
*
|
||||
*****************************************************************************/
|
||||
|
||||
/* the following should probably be moved elsewhere -ay */
|
||||
|
||||
TargetEntry *
|
||||
MakeTLE(Resdom *resdom, Node *expr)
|
||||
{
|
||||
TargetEntry *rt = makeNode(TargetEntry);
|
||||
rt->resdom = resdom;
|
||||
rt->expr = expr;
|
||||
return rt;
|
||||
}
|
||||
|
||||
Var *
|
||||
get_expr(TargetEntry *tle)
|
||||
{
|
||||
Assert(tle!=NULL);
|
||||
Assert(tle->expr!=NULL);
|
||||
|
||||
return ((Var *)tle->expr);
|
||||
}
|
||||
|
||||
#endif /* 0 */
|
||||
|
||||
|
||||
|
107
src/backend/optimizer/util/joininfo.c
Normal file
107
src/backend/optimizer/util/joininfo.c
Normal file
@@ -0,0 +1,107 @@
|
||||
/*-------------------------------------------------------------------------
|
||||
*
|
||||
* joininfo.c--
|
||||
* JoinInfo node manipulation routines
|
||||
*
|
||||
* Copyright (c) 1994, Regents of the University of California
|
||||
*
|
||||
*
|
||||
* IDENTIFICATION
|
||||
* $Header: /cvsroot/pgsql/src/backend/optimizer/util/joininfo.c,v 1.1.1.1 1996/07/09 06:21:38 scrappy Exp $
|
||||
*
|
||||
*-------------------------------------------------------------------------
|
||||
*/
|
||||
#include "postgres.h"
|
||||
|
||||
#include "nodes/relation.h"
|
||||
|
||||
#include "optimizer/internal.h"
|
||||
#include "optimizer/var.h"
|
||||
#include "optimizer/clauses.h"
|
||||
|
||||
|
||||
/*
|
||||
* joininfo-member--
|
||||
* Determines whether a node has already been created for a join
|
||||
* between a set of join relations and the relation described by
|
||||
* 'joininfo-list'.
|
||||
*
|
||||
* 'join-relids' is a list of relids corresponding to the join relation
|
||||
* 'joininfo-list' is the list of joininfo nodes against which this is
|
||||
* checked
|
||||
*
|
||||
* Returns the corresponding node in 'joininfo-list' if such a node
|
||||
* exists.
|
||||
*
|
||||
*/
|
||||
JInfo *
|
||||
joininfo_member(List *join_relids, List *joininfo_list)
|
||||
{
|
||||
List *i = NIL;
|
||||
List *other_rels = NIL;
|
||||
|
||||
foreach(i,joininfo_list) {
|
||||
other_rels = lfirst(i);
|
||||
if(same(join_relids, ((JInfo*)other_rels)->otherrels))
|
||||
return((JInfo*)other_rels);
|
||||
}
|
||||
return((JInfo*)NULL);
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* find-joininfo-node--
|
||||
* Find the joininfo node within a relation entry corresponding
|
||||
* to a join between 'this_rel' and the relations in 'join-relids'. A
|
||||
* new node is created and added to the relation entry's joininfo
|
||||
* field if the desired one can't be found.
|
||||
*
|
||||
* Returns a joininfo node.
|
||||
*
|
||||
*/
|
||||
JInfo *
|
||||
find_joininfo_node(Rel *this_rel, List *join_relids)
|
||||
{
|
||||
JInfo *joininfo = joininfo_member(join_relids,
|
||||
this_rel->joininfo);
|
||||
if( joininfo == NULL ) {
|
||||
joininfo = makeNode(JInfo);
|
||||
joininfo->otherrels = join_relids;
|
||||
joininfo->jinfoclauseinfo = NIL;
|
||||
joininfo->mergesortable = false;
|
||||
joininfo->hashjoinable = false;
|
||||
joininfo->inactive = false;
|
||||
this_rel->joininfo = lcons(joininfo, this_rel->joininfo);
|
||||
}
|
||||
return(joininfo);
|
||||
}
|
||||
|
||||
/*
|
||||
* other-join-clause-var--
|
||||
* Determines whether a var node is contained within a joinclause
|
||||
* of the form(op var var).
|
||||
*
|
||||
* Returns the other var node in the joinclause if it is, nil if not.
|
||||
*
|
||||
*/
|
||||
Var *
|
||||
other_join_clause_var(Var *var, Expr *clause)
|
||||
{
|
||||
Var *retval;
|
||||
Var *l, *r;
|
||||
|
||||
retval = (Var*) NULL;
|
||||
|
||||
if( var != NULL && join_clause_p((Node*)clause)) {
|
||||
l = (Var *) get_leftop(clause);
|
||||
r = (Var *) get_rightop(clause);
|
||||
|
||||
if(var_equal(var, l)) {
|
||||
retval = r;
|
||||
} else if(var_equal(var, r)) {
|
||||
retval = l;
|
||||
}
|
||||
}
|
||||
|
||||
return(retval);
|
||||
}
|
193
src/backend/optimizer/util/keys.c
Normal file
193
src/backend/optimizer/util/keys.c
Normal file
@@ -0,0 +1,193 @@
|
||||
/*-------------------------------------------------------------------------
|
||||
*
|
||||
* keys.c--
|
||||
* Key manipulation routines
|
||||
*
|
||||
* Copyright (c) 1994, Regents of the University of California
|
||||
*
|
||||
*
|
||||
* IDENTIFICATION
|
||||
* $Header: /cvsroot/pgsql/src/backend/optimizer/util/Attic/keys.c,v 1.1.1.1 1996/07/09 06:21:38 scrappy Exp $
|
||||
*
|
||||
*-------------------------------------------------------------------------
|
||||
*/
|
||||
#include "postgres.h"
|
||||
#include "nodes/pg_list.h"
|
||||
#include "nodes/nodes.h"
|
||||
#include "nodes/relation.h"
|
||||
#include "utils/elog.h"
|
||||
|
||||
#include "optimizer/internal.h"
|
||||
#include "optimizer/keys.h"
|
||||
#include "optimizer/tlist.h"
|
||||
|
||||
|
||||
static Expr *matching2_tlvar(int var, List *tlist, bool (*test)());
|
||||
|
||||
/*
|
||||
* 1. index key
|
||||
* one of:
|
||||
* attnum
|
||||
* (attnum arrayindex)
|
||||
* 2. path key
|
||||
* (subkey1 ... subkeyN)
|
||||
* where subkeyI is a var node
|
||||
* note that the 'Keys field is a list of these
|
||||
* 3. join key
|
||||
* (outer-subkey inner-subkey)
|
||||
* where each subkey is a var node
|
||||
* 4. sort key
|
||||
* one of:
|
||||
* SortKey node
|
||||
* number
|
||||
* nil
|
||||
* (may also refer to the 'SortKey field of a SortKey node,
|
||||
* which looks exactly like an index key)
|
||||
*
|
||||
*/
|
||||
|
||||
/*
|
||||
* match-indexkey-operand--
|
||||
* Returns t iff an index key 'index-key' matches the given clause
|
||||
* operand.
|
||||
*
|
||||
*/
|
||||
bool
|
||||
match_indexkey_operand(int indexkey, Var *operand, Rel *rel)
|
||||
{
|
||||
if (IsA (operand,Var) &&
|
||||
(lfirsti(rel->relids) == operand->varno) &&
|
||||
equal_indexkey_var(indexkey,operand))
|
||||
return(true);
|
||||
else
|
||||
return(false);
|
||||
}
|
||||
|
||||
/*
|
||||
* equal_indexkey_var--
|
||||
* Returns t iff an index key 'index-key' matches the corresponding
|
||||
* fields of var node 'var'.
|
||||
*
|
||||
*/
|
||||
bool
|
||||
equal_indexkey_var(int index_key, Var *var)
|
||||
{
|
||||
if (index_key == var->varattno)
|
||||
return(true);
|
||||
else
|
||||
return(false);
|
||||
}
|
||||
|
||||
/*
|
||||
* extract-subkey--
|
||||
* Returns the subkey in a join key corresponding to the outer or inner
|
||||
* lelation.
|
||||
*
|
||||
*/
|
||||
Var *
|
||||
extract_subkey(JoinKey *jk, int which_subkey)
|
||||
{
|
||||
Var *retval;
|
||||
|
||||
switch (which_subkey) {
|
||||
case OUTER:
|
||||
retval = jk->outer;
|
||||
break;
|
||||
case INNER:
|
||||
retval = jk->inner;
|
||||
break;
|
||||
default: /* do nothing */
|
||||
elog(DEBUG,"extract_subkey with neither INNER or OUTER");
|
||||
retval = NULL;
|
||||
}
|
||||
return(retval);
|
||||
}
|
||||
|
||||
/*
|
||||
* samekeys--
|
||||
* Returns t iff two sets of path keys are equivalent. They are
|
||||
* equivalent if the first subkey (var node) within each sublist of
|
||||
* list 'keys1' is contained within the corresponding sublist of 'keys2'.
|
||||
*
|
||||
* XXX It isn't necessary to check that each sublist exactly contain
|
||||
* the same elements because if the routine that built these
|
||||
* sublists together is correct, having one element in common
|
||||
* implies having all elements in common.
|
||||
*
|
||||
*/
|
||||
bool
|
||||
samekeys(List *keys1, List *keys2)
|
||||
{
|
||||
bool allmember = true;
|
||||
List *key1, *key2;
|
||||
|
||||
for(key1=keys1,key2=keys2 ; key1 != NIL && key2 !=NIL ;
|
||||
key1=lnext(key1), key2=lnext(key2))
|
||||
if (!member(lfirst(key1), lfirst(key2)))
|
||||
allmember = false;
|
||||
|
||||
if ( (length (keys2) >= length (keys1)) && allmember)
|
||||
return(true);
|
||||
else
|
||||
return(false);
|
||||
}
|
||||
|
||||
/*
|
||||
* collect-index-pathkeys--
|
||||
* Creates a list of subkeys by retrieving var nodes corresponding to
|
||||
* each index key in 'index-keys' from the relation's target list
|
||||
* 'tlist'. If the key is not in the target list, the key is irrelevant
|
||||
* and is thrown away. The returned subkey list is of the form:
|
||||
* ((var1) (var2) ... (varn))
|
||||
*
|
||||
* 'index-keys' is a list of index keys
|
||||
* 'tlist' is a relation target list
|
||||
*
|
||||
* Returns the list of cons'd subkeys.
|
||||
*
|
||||
*/
|
||||
/* This function is identical to matching_tlvar and tlistentry_member.
|
||||
* They should be merged.
|
||||
*/
|
||||
static Expr *
|
||||
matching2_tlvar(int var, List *tlist, bool (*test)())
|
||||
{
|
||||
TargetEntry *tlentry = NULL;
|
||||
|
||||
if (var) {
|
||||
List *temp;
|
||||
foreach (temp,tlist) {
|
||||
if ((*test)(var, get_expr(lfirst(temp)))) {
|
||||
tlentry = lfirst(temp);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (tlentry)
|
||||
return((Expr*)get_expr(tlentry));
|
||||
else
|
||||
return((Expr*)NULL);
|
||||
}
|
||||
|
||||
|
||||
List *
|
||||
collect_index_pathkeys(int *index_keys, List *tlist)
|
||||
{
|
||||
List *retval = NIL;
|
||||
|
||||
Assert (index_keys != NULL);
|
||||
|
||||
while(index_keys[0] != 0) {
|
||||
Expr *mvar;
|
||||
mvar = matching2_tlvar(index_keys[0],
|
||||
tlist,
|
||||
equal_indexkey_var);
|
||||
if (mvar)
|
||||
retval = nconc(retval,lcons(lcons(mvar,NIL),
|
||||
NIL));
|
||||
index_keys++;
|
||||
}
|
||||
return(retval);
|
||||
}
|
||||
|
117
src/backend/optimizer/util/ordering.c
Normal file
117
src/backend/optimizer/util/ordering.c
Normal file
@@ -0,0 +1,117 @@
|
||||
/*-------------------------------------------------------------------------
|
||||
*
|
||||
* ordering.c--
|
||||
* Routines to manipulate and compare merge and path orderings
|
||||
*
|
||||
* Copyright (c) 1994, Regents of the University of California
|
||||
*
|
||||
*
|
||||
* IDENTIFICATION
|
||||
* $Header: /cvsroot/pgsql/src/backend/optimizer/util/Attic/ordering.c,v 1.1.1.1 1996/07/09 06:21:38 scrappy Exp $
|
||||
*
|
||||
*-------------------------------------------------------------------------
|
||||
*/
|
||||
|
||||
#include "optimizer/internal.h"
|
||||
#include "optimizer/ordering.h"
|
||||
|
||||
|
||||
/*
|
||||
* equal-path-path-ordering--
|
||||
* Returns t iff two path orderings are equal.
|
||||
*
|
||||
*/
|
||||
bool
|
||||
equal_path_path_ordering(PathOrder *path_ordering1,
|
||||
PathOrder *path_ordering2)
|
||||
{
|
||||
if (path_ordering1 == path_ordering2)
|
||||
return true;
|
||||
|
||||
if (!path_ordering1 || !path_ordering2)
|
||||
return false;
|
||||
|
||||
if (path_ordering1->ordtype == MERGE_ORDER &&
|
||||
path_ordering2->ordtype == MERGE_ORDER) {
|
||||
|
||||
return equal(path_ordering1->ord.merge, path_ordering2->ord.merge);
|
||||
|
||||
} else if (path_ordering1->ordtype == SORTOP_ORDER &&
|
||||
path_ordering2->ordtype == SORTOP_ORDER) {
|
||||
|
||||
return
|
||||
(equal_sortops_order(path_ordering1->ord.sortop,
|
||||
path_ordering2->ord.sortop));
|
||||
} else if (path_ordering1->ordtype == MERGE_ORDER &&
|
||||
path_ordering2->ordtype == SORTOP_ORDER) {
|
||||
|
||||
return (path_ordering2->ord.sortop &&
|
||||
(path_ordering1->ord.merge->left_operator ==
|
||||
path_ordering2->ord.sortop[0]));
|
||||
} else {
|
||||
|
||||
return (path_ordering1->ord.sortop &&
|
||||
(path_ordering1->ord.sortop[0] ==
|
||||
path_ordering2->ord.merge->left_operator));
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* equal-path-merge-ordering--
|
||||
* Returns t iff a path ordering is usable for ordering a merge join.
|
||||
*
|
||||
* XXX Presently, this means that the first sortop of the path matches
|
||||
* either of the merge sortops. Is there a "right" and "wrong"
|
||||
* sortop to match?
|
||||
*
|
||||
*/
|
||||
bool
|
||||
equal_path_merge_ordering(Oid *path_ordering,
|
||||
MergeOrder *merge_ordering)
|
||||
{
|
||||
if (path_ordering == NULL || merge_ordering == NULL)
|
||||
return(false);
|
||||
|
||||
if (path_ordering[0] == merge_ordering->left_operator ||
|
||||
path_ordering[0] == merge_ordering->right_operator)
|
||||
return(true);
|
||||
else
|
||||
return(false);
|
||||
}
|
||||
|
||||
/*
|
||||
* equal-merge-merge-ordering--
|
||||
* Returns t iff two merge orderings are equal.
|
||||
*
|
||||
*/
|
||||
bool
|
||||
equal_merge_merge_ordering(MergeOrder *merge_ordering1,
|
||||
MergeOrder *merge_ordering2)
|
||||
{
|
||||
return (equal(merge_ordering1, merge_ordering2));
|
||||
}
|
||||
|
||||
/*****************************************************************************
|
||||
*
|
||||
*****************************************************************************/
|
||||
|
||||
/*
|
||||
* equal_sort_ops_order -
|
||||
* Returns true iff the sort operators are in the same order.
|
||||
*/
|
||||
bool
|
||||
equal_sortops_order(Oid *ordering1, Oid *ordering2)
|
||||
{
|
||||
int i = 0;
|
||||
|
||||
if (ordering1 == NULL || ordering2 == NULL)
|
||||
return (ordering1==ordering2);
|
||||
|
||||
while (ordering1[i]!=0 && ordering2[i]!=0) {
|
||||
if (ordering1[i] != ordering2[i])
|
||||
break;
|
||||
i++;
|
||||
}
|
||||
|
||||
return (ordering1[i]==0 && ordering2[i]==0);
|
||||
}
|
566
src/backend/optimizer/util/pathnode.c
Normal file
566
src/backend/optimizer/util/pathnode.c
Normal file
@@ -0,0 +1,566 @@
|
||||
/*-------------------------------------------------------------------------
|
||||
*
|
||||
* pathnode.c--
|
||||
* Routines to manipulate pathlists and create path nodes
|
||||
*
|
||||
* Copyright (c) 1994, Regents of the University of California
|
||||
*
|
||||
*
|
||||
* IDENTIFICATION
|
||||
* $Header: /cvsroot/pgsql/src/backend/optimizer/util/pathnode.c,v 1.1.1.1 1996/07/09 06:21:38 scrappy Exp $
|
||||
*
|
||||
*-------------------------------------------------------------------------
|
||||
*/
|
||||
#include <math.h>
|
||||
|
||||
#include "postgres.h"
|
||||
|
||||
#include "nodes/relation.h"
|
||||
#include "utils/elog.h"
|
||||
|
||||
#include "optimizer/internal.h"
|
||||
#include "optimizer/pathnode.h"
|
||||
#include "optimizer/clauseinfo.h"
|
||||
#include "optimizer/plancat.h"
|
||||
#include "optimizer/cost.h"
|
||||
#include "optimizer/keys.h"
|
||||
#include "optimizer/xfunc.h"
|
||||
#include "optimizer/ordering.h"
|
||||
|
||||
#include "parser/parsetree.h" /* for getrelid() */
|
||||
|
||||
static Path *better_path(Path *new_path, List *unique_paths, bool *noOther);
|
||||
|
||||
|
||||
/*****************************************************************************
|
||||
* MISC. PATH UTILITIES
|
||||
*****************************************************************************/
|
||||
|
||||
/*
|
||||
* path-is-cheaper--
|
||||
* Returns t iff 'path1' is cheaper than 'path2'.
|
||||
*
|
||||
*/
|
||||
bool
|
||||
path_is_cheaper(Path *path1, Path *path2)
|
||||
{
|
||||
Cost cost1 = path1->path_cost;
|
||||
Cost cost2 = path2->path_cost;
|
||||
|
||||
return((bool)(cost1 < cost2));
|
||||
}
|
||||
|
||||
/*
|
||||
* set_cheapest--
|
||||
* Finds the minimum cost path from among a relation's paths.
|
||||
*
|
||||
* 'parent-rel' is the parent relation
|
||||
* 'pathlist' is a list of path nodes corresponding to 'parent-rel'
|
||||
*
|
||||
* Returns and sets the relation entry field with the pathnode that
|
||||
* is minimum.
|
||||
*
|
||||
*/
|
||||
Path *
|
||||
set_cheapest(Rel *parent_rel, List *pathlist)
|
||||
{
|
||||
List *p;
|
||||
Path *cheapest_so_far;
|
||||
|
||||
Assert(pathlist!=NIL);
|
||||
Assert(IsA(parent_rel,Rel));
|
||||
|
||||
cheapest_so_far = (Path*)lfirst(pathlist);
|
||||
|
||||
foreach (p, lnext(pathlist)) {
|
||||
Path *path = (Path*)lfirst(p);
|
||||
|
||||
if (path_is_cheaper(path, cheapest_so_far)) {
|
||||
cheapest_so_far = path;
|
||||
}
|
||||
}
|
||||
|
||||
parent_rel->cheapestpath = cheapest_so_far;
|
||||
|
||||
return(cheapest_so_far);
|
||||
}
|
||||
|
||||
/*
|
||||
* add_pathlist--
|
||||
* For each path in the list 'new-paths', add to the list 'unique-paths'
|
||||
* only those paths that are unique (i.e., unique ordering and ordering
|
||||
* keys). Should a conflict arise, the more expensive path is thrown out,
|
||||
* thereby pruning the plan space. But we don't prune if xfunc
|
||||
* told us not to.
|
||||
*
|
||||
* 'parent-rel' is the relation entry to which these paths correspond.
|
||||
*
|
||||
* Returns the list of unique pathnodes.
|
||||
*
|
||||
*/
|
||||
List *
|
||||
add_pathlist(Rel *parent_rel, List *unique_paths, List *new_paths)
|
||||
{
|
||||
List *x;
|
||||
Path *new_path;
|
||||
Path *old_path;
|
||||
bool noOther;
|
||||
|
||||
foreach (x, new_paths) {
|
||||
new_path = (Path*)lfirst(x);
|
||||
if (member(new_path, unique_paths))
|
||||
continue;
|
||||
old_path = better_path(new_path,unique_paths,&noOther);
|
||||
|
||||
if (noOther) {
|
||||
/* Is a brand new path. */
|
||||
new_path->parent = parent_rel;
|
||||
unique_paths = lcons(new_path, unique_paths);
|
||||
} else if (old_path==NULL) {
|
||||
; /* do nothing if path is not cheaper */
|
||||
} else if (old_path != NULL) { /* (IsA(old_path,Path)) { */
|
||||
new_path->parent = parent_rel;
|
||||
if (!parent_rel->pruneable) {
|
||||
unique_paths = lcons(new_path, unique_paths);
|
||||
}else
|
||||
unique_paths = lcons(new_path,
|
||||
LispRemove(old_path,unique_paths));
|
||||
}
|
||||
}
|
||||
return(unique_paths);
|
||||
}
|
||||
|
||||
/*
|
||||
* better_path--
|
||||
* Determines whether 'new-path' has the same ordering and keys as some
|
||||
* path in the list 'unique-paths'. If there is a redundant path,
|
||||
* eliminate the more expensive path.
|
||||
*
|
||||
* Returns:
|
||||
* The old path - if 'new-path' matches some path in 'unique-paths' and is
|
||||
* cheaper
|
||||
* nil - if 'new-path' matches but isn't cheaper
|
||||
* t - if there is no path in the list with the same ordering and keys
|
||||
*
|
||||
*/
|
||||
static Path *
|
||||
better_path(Path *new_path, List *unique_paths, bool *noOther)
|
||||
{
|
||||
Path *old_path = (Path*)NULL;
|
||||
Path *path = (Path*)NULL;
|
||||
List *temp = NIL;
|
||||
Path *retval = NULL;
|
||||
|
||||
/* XXX - added the following two lines which weren't int
|
||||
* the lisp planner, but otherwise, doesn't seem to work
|
||||
* for the case where new_path is 'nil
|
||||
*/
|
||||
foreach (temp,unique_paths) {
|
||||
path = (Path*) lfirst(temp);
|
||||
|
||||
if ((equal_path_path_ordering(&new_path->p_ordering,
|
||||
&path->p_ordering) &&
|
||||
samekeys(new_path->keys, path->keys))) {
|
||||
old_path = path;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (old_path==NULL) {
|
||||
*noOther = true;
|
||||
} else {
|
||||
*noOther = false;
|
||||
if (path_is_cheaper(new_path,old_path)) {
|
||||
retval = old_path;
|
||||
}
|
||||
}
|
||||
|
||||
return(retval);
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*****************************************************************************
|
||||
* PATH NODE CREATION ROUTINES
|
||||
*****************************************************************************/
|
||||
|
||||
/*
|
||||
* create_seqscan_path--
|
||||
* Creates a path corresponding to a sequential scan, returning the
|
||||
* pathnode.
|
||||
*
|
||||
*/
|
||||
Path *
|
||||
create_seqscan_path(Rel *rel)
|
||||
{
|
||||
int relid=0;
|
||||
|
||||
Path *pathnode = makeNode(Path);
|
||||
|
||||
pathnode->pathtype = T_SeqScan;
|
||||
pathnode->parent = rel;
|
||||
pathnode->path_cost = 0.0;
|
||||
pathnode->p_ordering.ordtype = SORTOP_ORDER;
|
||||
pathnode->p_ordering.ord.sortop = NULL;
|
||||
pathnode->keys = NIL;
|
||||
/* copy clauseinfo list into path for expensive function processing
|
||||
* -- JMH, 7/7/92
|
||||
*/
|
||||
pathnode->locclauseinfo=
|
||||
(List*)copyObject((Node*)rel->clauseinfo);
|
||||
|
||||
if (rel->relids !=NULL)
|
||||
relid = lfirsti(rel->relids);
|
||||
|
||||
pathnode->path_cost = cost_seqscan (relid,
|
||||
rel->pages, rel->tuples);
|
||||
/* add in expensive functions cost! -- JMH, 7/7/92 */
|
||||
#if 0
|
||||
if (XfuncMode != XFUNC_OFF) {
|
||||
pathnode->path_cost +=
|
||||
xfunc_get_path_cost(pathnode));
|
||||
}
|
||||
#endif
|
||||
return (pathnode);
|
||||
}
|
||||
|
||||
/*
|
||||
* create_index_path--
|
||||
* Creates a single path node for an index scan.
|
||||
*
|
||||
* 'rel' is the parent rel
|
||||
* 'index' is the pathnode for the index on 'rel'
|
||||
* 'restriction-clauses' is a list of restriction clause nodes.
|
||||
* 'is-join-scan' is a flag indicating whether or not the index is being
|
||||
* considered because of its sort order.
|
||||
*
|
||||
* Returns the new path node.
|
||||
*
|
||||
*/
|
||||
IndexPath *
|
||||
create_index_path(Query *root,
|
||||
Rel *rel,
|
||||
Rel *index,
|
||||
List *restriction_clauses,
|
||||
bool is_join_scan)
|
||||
{
|
||||
IndexPath *pathnode = makeNode(IndexPath);
|
||||
|
||||
pathnode->path.pathtype = T_IndexScan;
|
||||
pathnode->path.parent = rel;
|
||||
pathnode->indexid = index->relids;
|
||||
|
||||
pathnode->path.p_ordering.ordtype = SORTOP_ORDER;
|
||||
pathnode->path.p_ordering.ord.sortop = index->ordering;
|
||||
pathnode->indexqual = NIL;
|
||||
|
||||
/* copy clauseinfo list into path for expensive function processing
|
||||
* -- JMH, 7/7/92
|
||||
*/
|
||||
pathnode->path.locclauseinfo =
|
||||
set_difference((List*) copyObject((Node*)rel->clauseinfo),
|
||||
(List*) restriction_clauses);
|
||||
|
||||
/*
|
||||
* The index must have an ordering for the path to have (ordering) keys,
|
||||
* and vice versa.
|
||||
*/
|
||||
if (pathnode->path.p_ordering.ord.sortop) {
|
||||
pathnode->path.keys = collect_index_pathkeys(index->indexkeys,
|
||||
rel->targetlist);
|
||||
/*
|
||||
* Check that the keys haven't 'disappeared', since they may
|
||||
* no longer be in the target list (i.e., index keys that are not
|
||||
* relevant to the scan are not applied to the scan path node,
|
||||
* so if no index keys were found, we can't order the path).
|
||||
*/
|
||||
if (pathnode->path.keys==NULL) {
|
||||
pathnode->path.p_ordering.ord.sortop = NULL;
|
||||
}
|
||||
} else {
|
||||
pathnode->path.keys = NULL;
|
||||
}
|
||||
|
||||
if (is_join_scan || restriction_clauses==NULL) {
|
||||
/*
|
||||
* Indices used for joins or sorting result nodes don't
|
||||
* restrict the result at all, they simply order it,
|
||||
* so compute the scan cost
|
||||
* accordingly -- use a selectivity of 1.0.
|
||||
*/
|
||||
/* is the statement above really true? what about IndexScan as the
|
||||
inner of a join? */
|
||||
pathnode->path.path_cost =
|
||||
cost_index (lfirsti(index->relids),
|
||||
index->pages,
|
||||
1.0,
|
||||
rel->pages,
|
||||
rel->tuples,
|
||||
index->pages,
|
||||
index->tuples,
|
||||
false);
|
||||
/* add in expensive functions cost! -- JMH, 7/7/92 */
|
||||
#if 0
|
||||
if (XfuncMode != XFUNC_OFF) {
|
||||
pathnode->path_cost =
|
||||
(pathnode->path_cost +
|
||||
xfunc_get_path_cost((Path*)pathnode));
|
||||
}
|
||||
#endif
|
||||
} else {
|
||||
/*
|
||||
* Compute scan cost for the case when 'index' is used with a
|
||||
* restriction clause.
|
||||
*/
|
||||
List *attnos;
|
||||
List *values;
|
||||
List *flags;
|
||||
float npages;
|
||||
float selec;
|
||||
Cost clausesel;
|
||||
|
||||
get_relattvals(restriction_clauses,
|
||||
&attnos,
|
||||
&values,
|
||||
&flags);
|
||||
index_selectivity(lfirsti(index->relids),
|
||||
index->classlist,
|
||||
get_opnos(restriction_clauses),
|
||||
getrelid(lfirsti(rel->relids),
|
||||
root->rtable),
|
||||
attnos,
|
||||
values,
|
||||
flags,
|
||||
length(restriction_clauses),
|
||||
&npages,
|
||||
&selec);
|
||||
/* each clause gets an equal selectivity */
|
||||
clausesel =
|
||||
pow(selec,
|
||||
1.0 / (double) length(restriction_clauses));
|
||||
|
||||
pathnode->indexqual = restriction_clauses;
|
||||
pathnode->path.path_cost =
|
||||
cost_index (lfirsti(index->relids),
|
||||
(int)npages,
|
||||
selec,
|
||||
rel->pages,
|
||||
rel->tuples,
|
||||
index->pages,
|
||||
index->tuples,
|
||||
false);
|
||||
|
||||
#if 0
|
||||
/* add in expensive functions cost! -- JMH, 7/7/92 */
|
||||
if (XfuncMode != XFUNC_OFF) {
|
||||
pathnode->path_cost +=
|
||||
xfunc_get_path_cost((Path*)pathnode);
|
||||
}
|
||||
#endif
|
||||
/* Set selectivities of clauses used with index to the selectivity
|
||||
* of this index, subdividing the selectivity equally over each of
|
||||
* the clauses.
|
||||
*/
|
||||
|
||||
/* XXX Can this divide the selectivities in a better way? */
|
||||
set_clause_selectivities(restriction_clauses, clausesel);
|
||||
}
|
||||
return(pathnode);
|
||||
}
|
||||
|
||||
/*
|
||||
* create_nestloop_path--
|
||||
* Creates a pathnode corresponding to a nestloop join between two
|
||||
* relations.
|
||||
*
|
||||
* 'joinrel' is the join relation.
|
||||
* 'outer_rel' is the outer join relation
|
||||
* 'outer_path' is the outer join path.
|
||||
* 'inner_path' is the inner join path.
|
||||
* 'keys' are the keys of the path
|
||||
*
|
||||
* Returns the resulting path node.
|
||||
*
|
||||
*/
|
||||
JoinPath *
|
||||
create_nestloop_path(Rel *joinrel,
|
||||
Rel *outer_rel,
|
||||
Path *outer_path,
|
||||
Path *inner_path,
|
||||
List *keys)
|
||||
{
|
||||
JoinPath *pathnode = makeNode(JoinPath);
|
||||
|
||||
pathnode->path.pathtype = T_NestLoop;
|
||||
pathnode->path.parent = joinrel;
|
||||
pathnode->outerjoinpath = outer_path;
|
||||
pathnode->innerjoinpath = inner_path;
|
||||
pathnode->pathclauseinfo = joinrel->clauseinfo;
|
||||
pathnode->path.keys = keys;
|
||||
pathnode->path.joinid = NIL;
|
||||
pathnode->path.outerjoincost = (Cost)0.0;
|
||||
pathnode->path.locclauseinfo = NIL;
|
||||
|
||||
if (keys) {
|
||||
pathnode->path.p_ordering.ordtype =
|
||||
outer_path->p_ordering.ordtype;
|
||||
if (outer_path->p_ordering.ordtype == SORTOP_ORDER) {
|
||||
pathnode->path.p_ordering.ord.sortop =
|
||||
outer_path->p_ordering.ord.sortop;
|
||||
} else {
|
||||
pathnode->path.p_ordering.ord.merge =
|
||||
outer_path->p_ordering.ord.merge;
|
||||
}
|
||||
} else {
|
||||
pathnode->path.p_ordering.ordtype = SORTOP_ORDER;
|
||||
pathnode->path.p_ordering.ord.sortop = NULL;
|
||||
}
|
||||
|
||||
pathnode->path.path_cost =
|
||||
cost_nestloop(outer_path->path_cost,
|
||||
inner_path->path_cost,
|
||||
outer_rel->size,
|
||||
inner_path->parent->size,
|
||||
page_size(outer_rel->size,
|
||||
outer_rel->width),
|
||||
IsA(inner_path,IndexPath));
|
||||
/* add in expensive function costs -- JMH 7/7/92 */
|
||||
#if 0
|
||||
if (XfuncMode != XFUNC_OFF) {
|
||||
pathnode->path_cost += xfunc_get_path_cost((Path*)pathnode);
|
||||
}
|
||||
#endif
|
||||
return(pathnode);
|
||||
}
|
||||
|
||||
/*
|
||||
* create_mergesort_path--
|
||||
* Creates a pathnode corresponding to a mergesort join between
|
||||
* two relations
|
||||
*
|
||||
* 'joinrel' is the join relation
|
||||
* 'outersize' is the number of tuples in the outer relation
|
||||
* 'innersize' is the number of tuples in the inner relation
|
||||
* 'outerwidth' is the number of bytes per tuple in the outer relation
|
||||
* 'innerwidth' is the number of bytes per tuple in the inner relation
|
||||
* 'outer_path' is the outer path
|
||||
* 'inner_path' is the inner path
|
||||
* 'keys' are the new keys of the join relation
|
||||
* 'order' is the sort order required for the merge
|
||||
* 'mergeclauses' are the applicable join/restriction clauses
|
||||
* 'outersortkeys' are the sort varkeys for the outer relation
|
||||
* 'innersortkeys' are the sort varkeys for the inner relation
|
||||
*
|
||||
*/
|
||||
MergePath *
|
||||
create_mergesort_path(Rel *joinrel,
|
||||
int outersize,
|
||||
int innersize,
|
||||
int outerwidth,
|
||||
int innerwidth,
|
||||
Path *outer_path,
|
||||
Path *inner_path,
|
||||
List *keys,
|
||||
MergeOrder *order,
|
||||
List *mergeclauses,
|
||||
List *outersortkeys,
|
||||
List *innersortkeys)
|
||||
{
|
||||
MergePath *pathnode = makeNode(MergePath);
|
||||
|
||||
pathnode->jpath.path.pathtype = T_MergeJoin;
|
||||
pathnode->jpath.path.parent = joinrel;
|
||||
pathnode->jpath.outerjoinpath = outer_path;
|
||||
pathnode->jpath.innerjoinpath = inner_path;
|
||||
pathnode->jpath.pathclauseinfo = joinrel->clauseinfo;
|
||||
pathnode->jpath.path.keys = keys;
|
||||
pathnode->jpath.path.p_ordering.ordtype = MERGE_ORDER;
|
||||
pathnode->jpath.path.p_ordering.ord.merge = order;
|
||||
pathnode->path_mergeclauses = mergeclauses;
|
||||
pathnode->jpath.path.locclauseinfo = NIL;
|
||||
pathnode->outersortkeys = outersortkeys;
|
||||
pathnode->innersortkeys = innersortkeys;
|
||||
pathnode->jpath.path.path_cost =
|
||||
cost_mergesort(outer_path->path_cost,
|
||||
inner_path->path_cost,
|
||||
outersortkeys,
|
||||
innersortkeys,
|
||||
outersize,
|
||||
innersize,
|
||||
outerwidth,
|
||||
innerwidth);
|
||||
/* add in expensive function costs -- JMH 7/7/92 */
|
||||
#if 0
|
||||
if (XfuncMode != XFUNC_OFF) {
|
||||
pathnode->path_cost +=
|
||||
xfunc_get_path_cost((Path*)pathnode);
|
||||
}
|
||||
#endif
|
||||
return(pathnode);
|
||||
}
|
||||
|
||||
/*
|
||||
* create_hashjoin_path-- XXX HASH
|
||||
* Creates a pathnode corresponding to a hash join between two relations.
|
||||
*
|
||||
* 'joinrel' is the join relation
|
||||
* 'outersize' is the number of tuples in the outer relation
|
||||
* 'innersize' is the number of tuples in the inner relation
|
||||
* 'outerwidth' is the number of bytes per tuple in the outer relation
|
||||
* 'innerwidth' is the number of bytes per tuple in the inner relation
|
||||
* 'outer_path' is the outer path
|
||||
* 'inner_path' is the inner path
|
||||
* 'keys' are the new keys of the join relation
|
||||
* 'operator' is the hashjoin operator
|
||||
* 'hashclauses' are the applicable join/restriction clauses
|
||||
* 'outerkeys' are the sort varkeys for the outer relation
|
||||
* 'innerkeys' are the sort varkeys for the inner relation
|
||||
*
|
||||
*/
|
||||
HashPath *
|
||||
create_hashjoin_path(Rel *joinrel,
|
||||
int outersize,
|
||||
int innersize,
|
||||
int outerwidth,
|
||||
int innerwidth,
|
||||
Path *outer_path,
|
||||
Path *inner_path,
|
||||
List *keys,
|
||||
Oid operator,
|
||||
List *hashclauses,
|
||||
List *outerkeys,
|
||||
List *innerkeys)
|
||||
{
|
||||
HashPath *pathnode = makeNode(HashPath);
|
||||
|
||||
pathnode->jpath.path.pathtype = T_HashJoin;
|
||||
pathnode->jpath.path.parent = joinrel;
|
||||
pathnode->jpath.outerjoinpath = outer_path;
|
||||
pathnode->jpath.innerjoinpath = inner_path;
|
||||
pathnode->jpath.pathclauseinfo = joinrel->clauseinfo;
|
||||
pathnode->jpath.path.locclauseinfo = NIL;
|
||||
pathnode->jpath.path.keys = keys;
|
||||
pathnode->jpath.path.p_ordering.ordtype = SORTOP_ORDER;
|
||||
pathnode->jpath.path.p_ordering.ord.sortop = NULL;
|
||||
pathnode->jpath.path.outerjoincost = (Cost)0.0;
|
||||
pathnode->jpath.path.joinid = (Relid)NULL;
|
||||
/* pathnode->hashjoinoperator = operator; */
|
||||
pathnode->path_hashclauses = hashclauses;
|
||||
pathnode->outerhashkeys = outerkeys;
|
||||
pathnode->innerhashkeys = innerkeys;
|
||||
pathnode->jpath.path.path_cost =
|
||||
cost_hashjoin(outer_path->path_cost,
|
||||
inner_path->path_cost,
|
||||
outerkeys,
|
||||
innerkeys,
|
||||
outersize,innersize,
|
||||
outerwidth,innerwidth);
|
||||
/* add in expensive function costs -- JMH 7/7/92 */
|
||||
#if 0
|
||||
if (XfuncMode != XFUNC_OFF) {
|
||||
pathnode->path_cost +=
|
||||
xfunc_get_path_cost((Path*)pathnode);
|
||||
}
|
||||
#endif
|
||||
return(pathnode);
|
||||
}
|
582
src/backend/optimizer/util/plancat.c
Normal file
582
src/backend/optimizer/util/plancat.c
Normal file
@@ -0,0 +1,582 @@
|
||||
/*-------------------------------------------------------------------------
|
||||
*
|
||||
* plancat.c--
|
||||
* routines for accessing the system catalogs
|
||||
*
|
||||
*
|
||||
* Copyright (c) 1994, Regents of the University of California
|
||||
*
|
||||
*
|
||||
* IDENTIFICATION
|
||||
* $Header: /cvsroot/pgsql/src/backend/optimizer/util/plancat.c,v 1.1.1.1 1996/07/09 06:21:39 scrappy Exp $
|
||||
*
|
||||
*-------------------------------------------------------------------------
|
||||
*/
|
||||
#include <stdio.h>
|
||||
#include "postgres.h"
|
||||
|
||||
#include "access/heapam.h"
|
||||
#include "access/genam.h"
|
||||
#include "access/htup.h"
|
||||
#include "access/itup.h"
|
||||
|
||||
#include "catalog/catname.h"
|
||||
#include "catalog/pg_amop.h"
|
||||
#include "catalog/pg_index.h"
|
||||
#include "catalog/pg_inherits.h"
|
||||
#include "catalog/pg_version.h"
|
||||
|
||||
#include "nodes/pg_list.h"
|
||||
#include "parser/parsetree.h" /* for getrelid() */
|
||||
#include "fmgr.h"
|
||||
|
||||
#include "optimizer/internal.h"
|
||||
#include "optimizer/plancat.h"
|
||||
|
||||
#include "utils/tqual.h"
|
||||
#include "utils/elog.h"
|
||||
#include "utils/palloc.h"
|
||||
#include "utils/syscache.h"
|
||||
|
||||
|
||||
static void IndexSelectivity(Oid indexrelid, Oid indrelid, int32 nIndexKeys,
|
||||
Oid AccessMethodOperatorClasses[], Oid operatorObjectIds[],
|
||||
int32 varAttributeNumbers[], char *constValues[], int32 constFlags[],
|
||||
float *idxPages, float *idxSelec);
|
||||
|
||||
|
||||
/*
|
||||
* relation-info -
|
||||
* Retrieves catalog information for a given relation. Given the oid of
|
||||
* the relation, return the following information:
|
||||
* whether the relation has secondary indices
|
||||
* number of pages
|
||||
* number of tuples
|
||||
*/
|
||||
void
|
||||
relation_info(Query *root, Index relid,
|
||||
bool *hasindex, int *pages, int *tuples)
|
||||
{
|
||||
HeapTuple relationTuple;
|
||||
Form_pg_class relation;
|
||||
Oid relationObjectId;
|
||||
|
||||
relationObjectId = getrelid(relid, root->rtable);
|
||||
relationTuple = SearchSysCacheTuple(RELOID,
|
||||
ObjectIdGetDatum(relationObjectId),
|
||||
0,0,0);
|
||||
if (HeapTupleIsValid(relationTuple)) {
|
||||
relation = (Form_pg_class)GETSTRUCT(relationTuple);
|
||||
|
||||
*hasindex = (relation->relhasindex) ? TRUE : FALSE;
|
||||
*pages = relation->relpages;
|
||||
*tuples = relation->reltuples;
|
||||
} else {
|
||||
elog(WARN, "RelationCatalogInformation: Relation %d not found",
|
||||
relationObjectId);
|
||||
}
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* index-info--
|
||||
* Retrieves catalog information on an index on a given relation.
|
||||
*
|
||||
* The index relation is opened on the first invocation. The current
|
||||
* retrieves the next index relation within the catalog that has not
|
||||
* already been retrieved by a previous call. The index catalog
|
||||
* is closed when no more indices for 'relid' can be found.
|
||||
*
|
||||
* 'first' is 1 if this is the first call
|
||||
*
|
||||
* Returns true if successful and false otherwise. Index info is returned
|
||||
* via the transient data structure 'info'.
|
||||
*
|
||||
*/
|
||||
bool
|
||||
index_info(Query *root, bool first, int relid, IdxInfoRetval *info)
|
||||
{
|
||||
register i;
|
||||
HeapTuple indexTuple, amopTuple;
|
||||
IndexTupleForm index;
|
||||
Relation indexRelation;
|
||||
uint16 amstrategy;
|
||||
Oid relam;
|
||||
Oid indrelid;
|
||||
|
||||
static Relation relation = (Relation) NULL;
|
||||
static HeapScanDesc scan = (HeapScanDesc) NULL;
|
||||
static ScanKeyData indexKey;
|
||||
|
||||
|
||||
/* find the oid of the indexed relation */
|
||||
indrelid = getrelid(relid, root->rtable);
|
||||
|
||||
memset(info, 0, sizeof(IdxInfoRetval));
|
||||
|
||||
/*
|
||||
* the maximum number of elements in each of the following arrays is
|
||||
* 8. We allocate one more for a terminating 0 to indicate the end
|
||||
* of the array.
|
||||
*/
|
||||
info->indexkeys = (int *)palloc(sizeof(int)*9);
|
||||
memset(info->indexkeys, 0, sizeof(int)*9);
|
||||
info->orderOprs = (Oid *)palloc(sizeof(Oid)*9);
|
||||
memset(info->orderOprs, 0, sizeof(Oid)*9);
|
||||
info->classlist = (Oid *)palloc(sizeof(Oid)*9);
|
||||
memset(info->classlist, 0, sizeof(Oid)*9);
|
||||
|
||||
/* Find an index on the given relation */
|
||||
if (first) {
|
||||
if (RelationIsValid(relation))
|
||||
heap_close(relation);
|
||||
if (HeapScanIsValid(scan))
|
||||
heap_endscan(scan);
|
||||
|
||||
ScanKeyEntryInitialize(&indexKey, 0,
|
||||
Anum_pg_index_indrelid,
|
||||
F_OIDEQ,
|
||||
ObjectIdGetDatum(indrelid));
|
||||
|
||||
relation = heap_openr(IndexRelationName);
|
||||
scan = heap_beginscan(relation, 0, NowTimeQual,
|
||||
1, &indexKey);
|
||||
}
|
||||
if (!HeapScanIsValid(scan))
|
||||
elog(WARN, "index_info: scan not started");
|
||||
indexTuple = heap_getnext(scan, 0, (Buffer *) NULL);
|
||||
if (!HeapTupleIsValid(indexTuple)) {
|
||||
heap_endscan(scan);
|
||||
heap_close(relation);
|
||||
scan = (HeapScanDesc) NULL;
|
||||
relation = (Relation) NULL;
|
||||
return(0);
|
||||
}
|
||||
|
||||
/* Extract info from the index tuple */
|
||||
index = (IndexTupleForm)GETSTRUCT(indexTuple);
|
||||
info->relid = index->indexrelid; /* index relation */
|
||||
for (i = 0; i < 8; i++)
|
||||
info->indexkeys[i] = index->indkey[i];
|
||||
for (i = 0; i < 8; i++)
|
||||
info->classlist[i] = index->indclass[i];
|
||||
|
||||
info->indproc = index->indproc; /* functional index ?? */
|
||||
|
||||
/* partial index ?? */
|
||||
if (VARSIZE(&index->indpred) != 0) {
|
||||
/*
|
||||
* The memory allocated here for the predicate (in lispReadString)
|
||||
* only needs to stay around until it's used in find_index_paths,
|
||||
* which is all within a command, so the automatic pfree at end
|
||||
* of transaction should be ok.
|
||||
*/
|
||||
char *predString;
|
||||
|
||||
predString = fmgr(F_TEXTOUT, &index->indpred);
|
||||
info->indpred = (Node*)stringToNode(predString);
|
||||
pfree(predString);
|
||||
}
|
||||
|
||||
/* Extract info from the relation descriptor for the index */
|
||||
indexRelation = index_open(index->indexrelid);
|
||||
#ifdef notdef
|
||||
/* XXX should iterate through strategies -- but how? use #1 for now */
|
||||
amstrategy = indexRelation->rd_am->amstrategies;
|
||||
#endif /* notdef */
|
||||
amstrategy = 1;
|
||||
relam = indexRelation->rd_rel->relam;
|
||||
info->relam = relam;
|
||||
info->pages = indexRelation->rd_rel->relpages;
|
||||
info->tuples = indexRelation->rd_rel->reltuples;
|
||||
heap_close(indexRelation);
|
||||
|
||||
/*
|
||||
* Find the index ordering keys
|
||||
*
|
||||
* Must use indclass to know when to stop looking since with
|
||||
* functional indices there could be several keys (args) for
|
||||
* one opclass. -mer 27 Sept 1991
|
||||
*/
|
||||
for (i = 0; i < 8 && index->indclass[i]; ++i) {
|
||||
amopTuple = SearchSysCacheTuple(AMOPSTRATEGY,
|
||||
ObjectIdGetDatum(relam),
|
||||
ObjectIdGetDatum(index->indclass[i]),
|
||||
UInt16GetDatum(amstrategy),
|
||||
0);
|
||||
if (!HeapTupleIsValid(amopTuple))
|
||||
elog(WARN, "index_info: no amop %d %d %d",
|
||||
relam, index->indclass[i], amstrategy);
|
||||
info->orderOprs[i] =
|
||||
((Form_pg_amop)GETSTRUCT(amopTuple))->amopopr;
|
||||
}
|
||||
return(TRUE);
|
||||
}
|
||||
|
||||
/*
|
||||
* index-selectivity--
|
||||
*
|
||||
* Call util/plancat.c:IndexSelectivity with the indicated arguments.
|
||||
*
|
||||
* 'indid' is the index OID
|
||||
* 'classes' is a list of index key classes
|
||||
* 'opnos' is a list of index key operator OIDs
|
||||
* 'relid' is the OID of the relation indexed
|
||||
* 'attnos' is a list of the relation attnos which the index keys over
|
||||
* 'values' is a list of the values of the clause's constants
|
||||
* 'flags' is a list of fixnums which describe the constants
|
||||
* 'nkeys' is the number of index keys
|
||||
*
|
||||
* Returns two floats: index pages and index selectivity in 'idxPages' and
|
||||
* 'idxSelec'.
|
||||
*
|
||||
*/
|
||||
void
|
||||
index_selectivity(Oid indid,
|
||||
Oid *classes,
|
||||
List *opnos,
|
||||
Oid relid,
|
||||
List *attnos,
|
||||
List *values,
|
||||
List *flags,
|
||||
int32 nkeys,
|
||||
float *idxPages,
|
||||
float *idxSelec)
|
||||
{
|
||||
Oid *opno_array;
|
||||
int *attno_array, *flag_array;
|
||||
char **value_array;
|
||||
int i = 0;
|
||||
List *xopno, *xattno, *value, *flag;
|
||||
|
||||
if (length(opnos)!=nkeys || length(attnos)!=nkeys ||
|
||||
length(values)!=nkeys || length(flags)!=nkeys) {
|
||||
|
||||
*idxPages = 0.0;
|
||||
*idxSelec = 1.0;
|
||||
return;
|
||||
}
|
||||
|
||||
opno_array = (Oid *)palloc(nkeys*sizeof(Oid));
|
||||
attno_array = (int *)palloc(nkeys*sizeof(int32));
|
||||
value_array = (char **)palloc(nkeys*sizeof(char *));
|
||||
flag_array = (int *)palloc(nkeys*sizeof(int32));
|
||||
|
||||
i = 0;
|
||||
foreach(xopno, opnos) {
|
||||
opno_array[i++] = (int)lfirst(xopno);
|
||||
}
|
||||
|
||||
i = 0;
|
||||
foreach(xattno,attnos) {
|
||||
attno_array[i++] = (int)lfirst(xattno);
|
||||
}
|
||||
|
||||
i = 0;
|
||||
foreach(value, values) {
|
||||
value_array[i++] = (char *)lfirst(value);
|
||||
}
|
||||
|
||||
i = 0;
|
||||
foreach(flag,flags) {
|
||||
flag_array[i++] = (int)lfirst(flag);
|
||||
}
|
||||
|
||||
IndexSelectivity(indid,
|
||||
relid,
|
||||
nkeys,
|
||||
classes, /* not used */
|
||||
opno_array,
|
||||
attno_array,
|
||||
value_array,
|
||||
flag_array,
|
||||
idxPages,
|
||||
idxSelec);
|
||||
return;
|
||||
}
|
||||
|
||||
/*
|
||||
* restriction_selectivity in lisp system.--
|
||||
*
|
||||
* NOTE: The routine is now merged with RestrictionClauseSelectivity
|
||||
* as defined in plancat.c
|
||||
*
|
||||
* Returns the selectivity of a specified operator.
|
||||
* This code executes registered procedures stored in the
|
||||
* operator relation, by calling the function manager.
|
||||
*
|
||||
* XXX The assumption in the selectivity procedures is that if the
|
||||
* relation OIDs or attribute numbers are -1, then the clause
|
||||
* isn't of the form (op var const).
|
||||
*/
|
||||
Cost
|
||||
restriction_selectivity(Oid functionObjectId,
|
||||
Oid operatorObjectId,
|
||||
Oid relationObjectId,
|
||||
AttrNumber attributeNumber,
|
||||
char *constValue,
|
||||
int32 constFlag)
|
||||
{
|
||||
float64 result;
|
||||
|
||||
result = (float64) fmgr(functionObjectId,
|
||||
(char *) operatorObjectId,
|
||||
(char *) relationObjectId,
|
||||
(char *) attributeNumber,
|
||||
(char *) constValue,
|
||||
(char *) constFlag,
|
||||
NULL);
|
||||
if (!PointerIsValid(result))
|
||||
elog(WARN, "RestrictionClauseSelectivity: bad pointer");
|
||||
|
||||
if (*result < 0.0 || *result > 1.0)
|
||||
elog(WARN, "RestrictionClauseSelectivity: bad value %lf",
|
||||
*result);
|
||||
|
||||
return ((Cost)*result);
|
||||
}
|
||||
|
||||
/*
|
||||
* join_selectivity--
|
||||
* Similarly, this routine is merged with JoinClauseSelectivity in
|
||||
* plancat.c
|
||||
*
|
||||
* Returns the selectivity of an operator, given the join clause
|
||||
* information.
|
||||
*
|
||||
* XXX The assumption in the selectivity procedures is that if the
|
||||
* relation OIDs or attribute numbers are -1, then the clause
|
||||
* isn't of the form (op var var).
|
||||
*/
|
||||
Cost
|
||||
join_selectivity (Oid functionObjectId,
|
||||
Oid operatorObjectId,
|
||||
Oid relationObjectId1,
|
||||
AttrNumber attributeNumber1,
|
||||
Oid relationObjectId2,
|
||||
AttrNumber attributeNumber2)
|
||||
{
|
||||
float64 result;
|
||||
|
||||
result = (float64) fmgr(functionObjectId,
|
||||
(char *) operatorObjectId,
|
||||
(char *) relationObjectId1,
|
||||
(char *) attributeNumber1,
|
||||
(char *) relationObjectId2,
|
||||
(char *) attributeNumber2,
|
||||
NULL);
|
||||
if (!PointerIsValid(result))
|
||||
elog(WARN, "JoinClauseSelectivity: bad pointer");
|
||||
|
||||
if (*result < 0.0 || *result > 1.0)
|
||||
elog(WARN, "JoinClauseSelectivity: bad value %lf",
|
||||
*result);
|
||||
|
||||
return((Cost)*result);
|
||||
}
|
||||
|
||||
/*
|
||||
* find_all_inheritors--
|
||||
*
|
||||
* Returns a LISP list containing the OIDs of all relations which
|
||||
* inherits from the relation with OID 'inhparent'.
|
||||
*/
|
||||
List *
|
||||
find_inheritance_children(Oid inhparent)
|
||||
{
|
||||
static ScanKeyData key[1] = {
|
||||
{ 0, Anum_pg_inherits_inhparent, F_OIDEQ }
|
||||
};
|
||||
|
||||
HeapTuple inheritsTuple;
|
||||
Relation relation;
|
||||
HeapScanDesc scan;
|
||||
List *list = NIL;
|
||||
Oid inhrelid;
|
||||
|
||||
fmgr_info(F_OIDEQ, &key[0].sk_func, &key[0].sk_nargs);
|
||||
|
||||
key[0].sk_argument = ObjectIdGetDatum((Oid)inhparent);
|
||||
relation = heap_openr(InheritsRelationName);
|
||||
scan = heap_beginscan(relation, 0, NowTimeQual, 1, key);
|
||||
while (HeapTupleIsValid(inheritsTuple =
|
||||
heap_getnext(scan, 0,
|
||||
(Buffer *) NULL))) {
|
||||
inhrelid = ((InheritsTupleForm)GETSTRUCT(inheritsTuple))->inhrel;
|
||||
list = lappendi(list, inhrelid);
|
||||
}
|
||||
heap_endscan(scan);
|
||||
heap_close(relation);
|
||||
return(list);
|
||||
}
|
||||
|
||||
/*
|
||||
* VersionGetParents--
|
||||
*
|
||||
* Returns a LISP list containing the OIDs of all relations which are
|
||||
* base relations of the relation with OID 'verrelid'.
|
||||
*/
|
||||
List *
|
||||
VersionGetParents(Oid verrelid)
|
||||
{
|
||||
static ScanKeyData key[1] = {
|
||||
{ 0, Anum_pg_version_verrelid, F_OIDEQ }
|
||||
};
|
||||
|
||||
HeapTuple versionTuple;
|
||||
Relation relation;
|
||||
HeapScanDesc scan;
|
||||
Oid verbaseid;
|
||||
List *list= NIL;
|
||||
|
||||
fmgr_info(F_OIDEQ, &key[0].sk_func, &key[0].sk_nargs);
|
||||
relation = heap_openr(VersionRelationName);
|
||||
key[0].sk_argument = ObjectIdGetDatum(verrelid);
|
||||
scan = heap_beginscan(relation, 0, NowTimeQual, 1, key);
|
||||
for (;;) {
|
||||
versionTuple = heap_getnext(scan, 0,
|
||||
(Buffer *) NULL);
|
||||
if (!HeapTupleIsValid(versionTuple))
|
||||
break;
|
||||
verbaseid = ((VersionTupleForm)
|
||||
GETSTRUCT(versionTuple))->verbaseid;
|
||||
|
||||
list = lconsi(verbaseid, list);
|
||||
|
||||
key[0].sk_argument = ObjectIdGetDatum(verbaseid);
|
||||
heap_rescan(scan, 0, key);
|
||||
}
|
||||
heap_endscan(scan);
|
||||
heap_close(relation);
|
||||
return(list);
|
||||
}
|
||||
|
||||
/*****************************************************************************
|
||||
*
|
||||
*****************************************************************************/
|
||||
|
||||
/*
|
||||
* IdexSelectivity--
|
||||
*
|
||||
* Retrieves the 'amopnpages' and 'amopselect' parameters for each
|
||||
* AM operator when a given index (specified by 'indexrelid') is used.
|
||||
* These two parameters are returned by copying them to into an array of
|
||||
* floats.
|
||||
*
|
||||
* Assumption: the attribute numbers and operator ObjectIds are in order
|
||||
* WRT to each other (otherwise, you have no way of knowing which
|
||||
* AM operator class or attribute number corresponds to which operator.
|
||||
*
|
||||
* 'varAttributeNumbers' contains attribute numbers for variables
|
||||
* 'constValues' contains the constant values
|
||||
* 'constFlags' describes how to treat the constants in each clause
|
||||
* 'nIndexKeys' describes how many keys the index actually has
|
||||
*
|
||||
* Returns 'selectivityInfo' filled with the sum of all pages touched
|
||||
* and the product of each clause's selectivity.
|
||||
*
|
||||
*/
|
||||
static void
|
||||
IndexSelectivity(Oid indexrelid,
|
||||
Oid indrelid,
|
||||
int32 nIndexKeys,
|
||||
Oid AccessMethodOperatorClasses[], /* XXX not used? */
|
||||
Oid operatorObjectIds[],
|
||||
int32 varAttributeNumbers[],
|
||||
char *constValues[],
|
||||
int32 constFlags[],
|
||||
float *idxPages,
|
||||
float *idxSelec)
|
||||
{
|
||||
register i, n;
|
||||
HeapTuple indexTuple, amopTuple, indRel;
|
||||
IndexTupleForm index;
|
||||
Form_pg_amop amop;
|
||||
Oid indclass;
|
||||
float64data npages, select;
|
||||
float64 amopnpages, amopselect;
|
||||
Oid relam;
|
||||
|
||||
indRel = SearchSysCacheTuple(RELOID,
|
||||
ObjectIdGetDatum(indexrelid),
|
||||
0,0,0);
|
||||
if (!HeapTupleIsValid(indRel))
|
||||
elog(WARN, "IndexSelectivity: index %d not found",
|
||||
indexrelid);
|
||||
relam = ((Form_pg_class)GETSTRUCT(indRel))->relam;
|
||||
|
||||
indexTuple = SearchSysCacheTuple(INDEXRELID,
|
||||
ObjectIdGetDatum(indexrelid),
|
||||
0,0,0);
|
||||
if (!HeapTupleIsValid(indexTuple))
|
||||
elog(WARN, "IndexSelectivity: index %d not found",
|
||||
indexrelid);
|
||||
index = (IndexTupleForm)GETSTRUCT(indexTuple);
|
||||
|
||||
npages = 0.0;
|
||||
select = 1.0;
|
||||
for (n = 0; n < nIndexKeys; ++n) {
|
||||
/*
|
||||
* Find the AM class for this key.
|
||||
*
|
||||
* If the first attribute number is invalid then we have a
|
||||
* functional index, and AM class is the first one defined
|
||||
* since functional indices have exactly one key.
|
||||
*/
|
||||
indclass = (varAttributeNumbers[0] == InvalidAttrNumber) ?
|
||||
index->indclass[0] : InvalidOid;
|
||||
i = 0;
|
||||
while ((i < nIndexKeys) && (indclass == InvalidOid)) {
|
||||
if (varAttributeNumbers[n] == index->indkey[i]) {
|
||||
indclass = index->indclass[i];
|
||||
break;
|
||||
}
|
||||
i++;
|
||||
}
|
||||
if (!OidIsValid(indclass)) {
|
||||
/*
|
||||
* Presumably this means that we are using a functional
|
||||
* index clause and so had no variable to match to
|
||||
* the index key ... if not we are in trouble.
|
||||
*/
|
||||
elog(NOTICE, "IndexSelectivity: no key %d in index %d",
|
||||
varAttributeNumbers[n], indexrelid);
|
||||
continue;
|
||||
}
|
||||
|
||||
amopTuple = SearchSysCacheTuple(AMOPOPID,
|
||||
ObjectIdGetDatum(indclass),
|
||||
ObjectIdGetDatum(operatorObjectIds[n]),
|
||||
ObjectIdGetDatum(relam),
|
||||
0);
|
||||
if (!HeapTupleIsValid(amopTuple))
|
||||
elog(WARN, "IndexSelectivity: no amop %d %d",
|
||||
indclass, operatorObjectIds[n]);
|
||||
amop = (Form_pg_amop)GETSTRUCT(amopTuple);
|
||||
amopnpages = (float64) fmgr(amop->amopnpages,
|
||||
(char *) operatorObjectIds[n],
|
||||
(char *) indrelid,
|
||||
(char *) varAttributeNumbers[n],
|
||||
(char *) constValues[n],
|
||||
(char *) constFlags[n],
|
||||
(char *) nIndexKeys,
|
||||
(char *) indexrelid);
|
||||
npages += PointerIsValid(amopnpages) ? *amopnpages : 0.0;
|
||||
if ((i = npages) < npages) /* ceil(npages)? */
|
||||
npages += 1.0;
|
||||
amopselect = (float64) fmgr(amop->amopselect,
|
||||
(char *) operatorObjectIds[n],
|
||||
(char *) indrelid,
|
||||
(char *) varAttributeNumbers[n],
|
||||
(char *) constValues[n],
|
||||
(char *) constFlags[n],
|
||||
(char *) nIndexKeys,
|
||||
(char *) indexrelid);
|
||||
select *= PointerIsValid(amopselect) ? *amopselect : 1.0;
|
||||
}
|
||||
*idxPages = npages;
|
||||
*idxSelec = select;
|
||||
}
|
||||
|
123
src/backend/optimizer/util/relnode.c
Normal file
123
src/backend/optimizer/util/relnode.c
Normal file
@@ -0,0 +1,123 @@
|
||||
/*-------------------------------------------------------------------------
|
||||
*
|
||||
* relnode.c--
|
||||
* Relation manipulation routines
|
||||
*
|
||||
* Copyright (c) 1994, Regents of the University of California
|
||||
*
|
||||
*
|
||||
* IDENTIFICATION
|
||||
* $Header: /cvsroot/pgsql/src/backend/optimizer/util/relnode.c,v 1.1.1.1 1996/07/09 06:21:39 scrappy Exp $
|
||||
*
|
||||
*-------------------------------------------------------------------------
|
||||
*/
|
||||
#include "postgres.h"
|
||||
|
||||
#include "nodes/relation.h"
|
||||
|
||||
#include "optimizer/internal.h"
|
||||
#include "optimizer/pathnode.h" /* where the decls go */
|
||||
#include "optimizer/plancat.h"
|
||||
|
||||
|
||||
|
||||
/*
|
||||
* get_base_rel--
|
||||
* Returns relation entry corresponding to 'relid', creating a new one if
|
||||
* necessary. This is for base relations.
|
||||
*
|
||||
*/
|
||||
Rel *get_base_rel(Query* root, int relid)
|
||||
{
|
||||
List *relids;
|
||||
Rel *rel;
|
||||
|
||||
relids = lconsi(relid, NIL);
|
||||
rel = rel_member(relids, root->base_relation_list_);
|
||||
if (rel==NULL) {
|
||||
rel = makeNode(Rel);
|
||||
rel->relids = relids;
|
||||
rel->indexed = false;
|
||||
rel->pages = 0;
|
||||
rel->tuples = 0;
|
||||
rel->width = 0;
|
||||
rel->targetlist = NIL;
|
||||
rel->pathlist = NIL;
|
||||
rel->unorderedpath = (Path *)NULL;
|
||||
rel->cheapestpath = (Path *)NULL;
|
||||
rel->pruneable = true;
|
||||
rel->classlist = NULL;
|
||||
rel->ordering = NULL;
|
||||
rel->relam = InvalidOid;
|
||||
rel->clauseinfo = NIL;
|
||||
rel->joininfo = NIL;
|
||||
rel->innerjoin = NIL;
|
||||
rel->superrels = NIL;
|
||||
|
||||
root->base_relation_list_ = lcons(rel,
|
||||
root->base_relation_list_);
|
||||
|
||||
/*
|
||||
* ??? the old lispy C code (get_rel) do a listp(relid) here but
|
||||
* that can never happen since we already established relid is not
|
||||
* a list. -ay 10/94
|
||||
*/
|
||||
if(relid < 0) {
|
||||
/*
|
||||
* If the relation is a materialized relation, assume
|
||||
* constants for sizes.
|
||||
*/
|
||||
rel->pages = _TEMP_RELATION_PAGES_;
|
||||
rel->tuples = _TEMP_RELATION_TUPLES_;
|
||||
|
||||
} else {
|
||||
bool hasindex;
|
||||
int pages, tuples;
|
||||
|
||||
/*
|
||||
* Otherwise, retrieve relation characteristics from the
|
||||
* system catalogs.
|
||||
*/
|
||||
relation_info(root, relid, &hasindex, &pages, &tuples);
|
||||
rel->indexed = hasindex;
|
||||
rel->pages = pages;
|
||||
rel->tuples = tuples;
|
||||
}
|
||||
}
|
||||
return rel;
|
||||
}
|
||||
|
||||
/*
|
||||
* get_join_rel--
|
||||
* Returns relation entry corresponding to 'relid' (a list of relids),
|
||||
* creating a new one if necessary. This is for join relations.
|
||||
*
|
||||
*/
|
||||
Rel *get_join_rel(Query *root, List *relid)
|
||||
{
|
||||
return rel_member(relid, root->join_relation_list_);
|
||||
}
|
||||
|
||||
/*
|
||||
* rel-member--
|
||||
* Determines whether a relation of id 'relid' is contained within a list
|
||||
* 'rels'.
|
||||
*
|
||||
* Returns the corresponding entry in 'rels' if it is there.
|
||||
*
|
||||
*/
|
||||
Rel *
|
||||
rel_member(List *relid, List *rels)
|
||||
{
|
||||
List *temp = NIL;
|
||||
List *temprelid = NIL;
|
||||
|
||||
if (relid!=NIL && rels!=NIL) {
|
||||
foreach(temp,rels) {
|
||||
temprelid = ((Rel*)lfirst(temp))->relids;
|
||||
if(same(temprelid, relid))
|
||||
return((Rel*)(lfirst(temp)));
|
||||
}
|
||||
}
|
||||
return(NULL);
|
||||
}
|
577
src/backend/optimizer/util/tlist.c
Normal file
577
src/backend/optimizer/util/tlist.c
Normal file
@@ -0,0 +1,577 @@
|
||||
/*-------------------------------------------------------------------------
|
||||
*
|
||||
* tlist.c--
|
||||
* Target list manipulation routines
|
||||
*
|
||||
* Copyright (c) 1994, Regents of the University of California
|
||||
*
|
||||
*
|
||||
* IDENTIFICATION
|
||||
* $Header: /cvsroot/pgsql/src/backend/optimizer/util/tlist.c,v 1.1.1.1 1996/07/09 06:21:39 scrappy Exp $
|
||||
*
|
||||
*-------------------------------------------------------------------------
|
||||
*/
|
||||
#include "postgres.h"
|
||||
|
||||
#include "nodes/relation.h"
|
||||
#include "nodes/primnodes.h"
|
||||
#include "nodes/pg_list.h"
|
||||
#include "nodes/nodeFuncs.h"
|
||||
#include "utils/elog.h"
|
||||
#include "utils/lsyscache.h"
|
||||
|
||||
#include "optimizer/internal.h"
|
||||
#include "optimizer/var.h"
|
||||
#include "optimizer/tlist.h"
|
||||
#include "optimizer/clauses.h"
|
||||
|
||||
#include "nodes/makefuncs.h"
|
||||
#include "parser/catalog_utils.h"
|
||||
|
||||
static Node *flatten_tlistentry(Node *tlistentry, List *flat_tlist);
|
||||
|
||||
/*****************************************************************************
|
||||
* ---------- RELATION node target list routines ----------
|
||||
*****************************************************************************/
|
||||
|
||||
/*
|
||||
* tlistentry-member--
|
||||
*
|
||||
* RETURNS: the leftmost member of sequence "targetlist" that satisfies
|
||||
* the predicate "var_equal"
|
||||
* MODIFIES: nothing
|
||||
* REQUIRES: test = function which can operate on a lispval union
|
||||
* var = valid var-node
|
||||
* targetlist = valid sequence
|
||||
*/
|
||||
TargetEntry *
|
||||
tlistentry_member(Var *var, List *targetlist)
|
||||
{
|
||||
if (var) {
|
||||
List *temp = NIL;
|
||||
|
||||
foreach (temp,targetlist) {
|
||||
if (var_equal(var,
|
||||
get_expr(lfirst(temp))))
|
||||
return((TargetEntry*)lfirst(temp));
|
||||
}
|
||||
}
|
||||
return (NULL);
|
||||
}
|
||||
|
||||
/*
|
||||
* matching_tlvar--
|
||||
*
|
||||
* RETURNS: var node in a target list which is var_equal to 'var',
|
||||
* if one exists.
|
||||
* REQUIRES: "test" operates on lispval unions,
|
||||
*
|
||||
*/
|
||||
Expr *
|
||||
matching_tlvar(Var *var, List *targetlist)
|
||||
{
|
||||
TargetEntry *tlentry;
|
||||
|
||||
tlentry = tlistentry_member(var,targetlist);
|
||||
if (tlentry)
|
||||
return((Expr*)get_expr (tlentry) );
|
||||
|
||||
return((Expr*) NULL);
|
||||
}
|
||||
|
||||
/*
|
||||
* add_tl_element--
|
||||
* Creates a targetlist entry corresponding to the supplied var node
|
||||
*
|
||||
* 'var' and adds the new targetlist entry to the targetlist field of
|
||||
* 'rel'
|
||||
*
|
||||
* RETURNS: nothing
|
||||
* MODIFIES: vartype and varid fields of leftmost varnode that matches
|
||||
* argument "var" (sometimes).
|
||||
* CREATES: new var-node iff no matching var-node exists in targetlist
|
||||
*/
|
||||
void
|
||||
add_tl_element(Rel *rel, Var *var)
|
||||
{
|
||||
Expr *oldvar = (Expr *)NULL;
|
||||
|
||||
oldvar = matching_tlvar(var, rel->targetlist);
|
||||
|
||||
/*
|
||||
* If 'var' is not already in 'rel's target list, add a new node.
|
||||
*/
|
||||
if (oldvar==NULL) {
|
||||
List *tlist = rel->targetlist;
|
||||
Var *newvar = makeVar(var->varno,
|
||||
var->varattno,
|
||||
var->vartype,
|
||||
var->varno,
|
||||
var->varoattno);
|
||||
|
||||
rel->targetlist =
|
||||
lappend (tlist,
|
||||
create_tl_element(newvar,
|
||||
length(tlist) + 1));
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* create_tl_element--
|
||||
* Creates a target list entry node and its associated (resdom var) pair
|
||||
* with its resdom number equal to 'resdomno' and the joinlist field set
|
||||
* to 'joinlist'.
|
||||
*
|
||||
* RETURNS: newly created tlist-entry
|
||||
* CREATES: new targetlist entry (always).
|
||||
*/
|
||||
TargetEntry*
|
||||
create_tl_element(Var *var, int resdomno)
|
||||
{
|
||||
TargetEntry *tlelement= makeNode(TargetEntry);
|
||||
|
||||
tlelement->resdom =
|
||||
makeResdom(resdomno,
|
||||
var->vartype,
|
||||
get_typlen(var->vartype),
|
||||
NULL,
|
||||
(Index)0,
|
||||
(Oid)0,
|
||||
0);
|
||||
tlelement->expr = (Node*)var;
|
||||
|
||||
return(tlelement);
|
||||
}
|
||||
|
||||
/*
|
||||
* get-actual-tlist--
|
||||
* Returns the targetlist elements from a relation tlist.
|
||||
*
|
||||
*/
|
||||
List *
|
||||
get_actual_tlist(List *tlist)
|
||||
{
|
||||
/*
|
||||
* this function is not making sense. - ay 10/94
|
||||
*/
|
||||
#if 0
|
||||
List *element = NIL;
|
||||
List *result = NIL;
|
||||
|
||||
if (tlist==NULL) {
|
||||
elog(DEBUG,"calling get_actual_tlist with empty tlist");
|
||||
return(NIL);
|
||||
}
|
||||
/* XXX - it is unclear to me what exactly get_entry
|
||||
should be doing, as it is unclear to me the exact
|
||||
relationship between "TL" "TLE" and joinlists */
|
||||
|
||||
foreach(element,tlist)
|
||||
result = lappend(result, lfirst((List*)lfirst(element)));
|
||||
|
||||
return(result);
|
||||
#endif
|
||||
return tlist;
|
||||
}
|
||||
|
||||
/*****************************************************************************
|
||||
* ---------- GENERAL target list routines ----------
|
||||
*****************************************************************************/
|
||||
|
||||
/*
|
||||
* tlist-member--
|
||||
* Determines whether a var node is already contained within a
|
||||
* target list.
|
||||
*
|
||||
* 'var' is the var node
|
||||
* 'tlist' is the target list
|
||||
* 'dots' is t if we must match dotfields to determine uniqueness
|
||||
*
|
||||
* Returns the resdom entry of the matching var node.
|
||||
*
|
||||
*/
|
||||
Resdom *
|
||||
tlist_member(Var *var, List *tlist)
|
||||
{
|
||||
List *i = NIL;
|
||||
TargetEntry *temp_tle = (TargetEntry *)NULL;
|
||||
TargetEntry *tl_elt = (TargetEntry *)NULL;
|
||||
|
||||
if (var) {
|
||||
foreach (i,tlist) {
|
||||
temp_tle = (TargetEntry *)lfirst(i);
|
||||
if (var_equal(var, get_expr(temp_tle))) {
|
||||
tl_elt = temp_tle;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (tl_elt != NULL)
|
||||
return(tl_elt->resdom);
|
||||
else
|
||||
return((Resdom*)NULL);
|
||||
}
|
||||
return ((Resdom*)NULL);
|
||||
}
|
||||
|
||||
/*
|
||||
* Routine to get the resdom out of a targetlist.
|
||||
*/
|
||||
Resdom *
|
||||
tlist_resdom(List *tlist, Resdom *resnode)
|
||||
{
|
||||
Resdom *resdom = (Resdom*)NULL;
|
||||
List *i = NIL;
|
||||
TargetEntry *temp_tle = (TargetEntry *)NULL;
|
||||
|
||||
foreach(i,tlist) {
|
||||
temp_tle = (TargetEntry *)lfirst(i);
|
||||
resdom = temp_tle->resdom;
|
||||
/* Since resnos are supposed to be unique */
|
||||
if (resnode->resno == resdom->resno)
|
||||
return(resdom);
|
||||
}
|
||||
return((Resdom*)NULL);
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* match_varid--
|
||||
* Searches a target list for an entry with some desired varid.
|
||||
*
|
||||
* 'varid' is the desired id
|
||||
* 'tlist' is the target list that is searched
|
||||
*
|
||||
* Returns the target list entry (resdom var) of the matching var.
|
||||
*
|
||||
* Now checks to make sure array references (in addition to range
|
||||
* table indices) are identical - retrieve (a.b[1],a.b[2]) should
|
||||
* not be turned into retrieve (a.b[1],a.b[1]).
|
||||
*
|
||||
* [what used to be varid is now broken up into two fields varnoold and
|
||||
* varoattno. Also, nested attnos are long gone. - ay 2/95]
|
||||
*/
|
||||
TargetEntry *
|
||||
match_varid(Var *test_var, List *tlist)
|
||||
{
|
||||
List *tl;
|
||||
Oid type_var;
|
||||
|
||||
type_var = (Oid) test_var->vartype;
|
||||
|
||||
foreach (tl, tlist) {
|
||||
TargetEntry *entry;
|
||||
Var *tlvar;
|
||||
|
||||
entry = lfirst(tl);
|
||||
tlvar = get_expr(entry);
|
||||
|
||||
/*
|
||||
* we test the original varno (instead of varno which might
|
||||
* be changed to INNER/OUTER.
|
||||
*/
|
||||
if (tlvar->varnoold == test_var->varnoold &&
|
||||
tlvar->varoattno == test_var->varoattno) {
|
||||
|
||||
if (tlvar->vartype == type_var)
|
||||
return(entry);
|
||||
}
|
||||
}
|
||||
|
||||
return (NULL);
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* new-unsorted-tlist--
|
||||
* Creates a copy of a target list by creating new resdom nodes
|
||||
* without sort information.
|
||||
*
|
||||
* 'targetlist' is the target list to be copied.
|
||||
*
|
||||
* Returns the resulting target list.
|
||||
*
|
||||
*/
|
||||
List *
|
||||
new_unsorted_tlist(List *targetlist)
|
||||
{
|
||||
List *new_targetlist = (List*)copyObject ((Node*)targetlist);
|
||||
List *x = NIL;
|
||||
|
||||
foreach (x, new_targetlist) {
|
||||
TargetEntry *tle = (TargetEntry *)lfirst(x);
|
||||
tle->resdom->reskey = 0;
|
||||
tle->resdom->reskeyop = (Oid)0;
|
||||
}
|
||||
return(new_targetlist);
|
||||
}
|
||||
|
||||
/*
|
||||
* copy-vars--
|
||||
* Replaces the var nodes in the first target list with those from
|
||||
* the second target list. The two target lists are assumed to be
|
||||
* identical except their actual resdoms and vars are different.
|
||||
*
|
||||
* 'target' is the target list to be replaced
|
||||
* 'source' is the target list to be copied
|
||||
*
|
||||
* Returns a new target list.
|
||||
*
|
||||
*/
|
||||
List *
|
||||
copy_vars(List *target, List *source)
|
||||
{
|
||||
List *result = NIL;
|
||||
List *src = NIL;
|
||||
List *dest = NIL;
|
||||
|
||||
for ( src = source, dest = target; src != NIL &&
|
||||
dest != NIL; src = lnext(src), dest = lnext(dest)) {
|
||||
TargetEntry *temp = MakeTLE(((TargetEntry *)lfirst(dest))->resdom,
|
||||
(Node*)get_expr(lfirst(src)));
|
||||
result = lappend(result,temp);
|
||||
}
|
||||
return(result);
|
||||
}
|
||||
|
||||
/*
|
||||
* flatten-tlist--
|
||||
* Create a target list that only contains unique variables.
|
||||
*
|
||||
*
|
||||
* 'tlist' is the current target list
|
||||
*
|
||||
* Returns the "flattened" new target list.
|
||||
*
|
||||
*/
|
||||
List *
|
||||
flatten_tlist(List *tlist)
|
||||
{
|
||||
int last_resdomno = 1;
|
||||
List *new_tlist = NIL;
|
||||
List *tlist_vars = NIL;
|
||||
List *temp;
|
||||
|
||||
foreach (temp, tlist) {
|
||||
TargetEntry *temp_entry = NULL;
|
||||
List *vars;
|
||||
|
||||
temp_entry = lfirst(temp);
|
||||
vars = pull_var_clause((Node*)get_expr(temp_entry));
|
||||
if(vars != NULL) {
|
||||
tlist_vars = nconc(tlist_vars, vars);
|
||||
}
|
||||
}
|
||||
|
||||
foreach (temp, tlist_vars) {
|
||||
Var *var = lfirst(temp);
|
||||
if (!(tlist_member(var, new_tlist))) {
|
||||
Resdom *r;
|
||||
|
||||
r = makeResdom(last_resdomno,
|
||||
var->vartype,
|
||||
get_typlen(var->vartype),
|
||||
NULL,
|
||||
(Index)0,
|
||||
(Oid)0,
|
||||
0);
|
||||
last_resdomno++;
|
||||
new_tlist = lappend(new_tlist, MakeTLE (r, (Node*)var));
|
||||
}
|
||||
}
|
||||
|
||||
return new_tlist;
|
||||
}
|
||||
|
||||
/*
|
||||
* flatten-tlist-vars--
|
||||
* Redoes the target list of a query with no nested attributes by
|
||||
* replacing vars within computational expressions with vars from
|
||||
* the 'flattened' target list of the query.
|
||||
*
|
||||
* 'full-tlist' is the actual target list
|
||||
* 'flat-tlist' is the flattened (var-only) target list
|
||||
*
|
||||
* Returns the modified actual target list.
|
||||
*
|
||||
*/
|
||||
List *
|
||||
flatten_tlist_vars(List *full_tlist, List *flat_tlist)
|
||||
{
|
||||
List *x = NIL;
|
||||
List *result = NIL;
|
||||
|
||||
foreach(x,full_tlist) {
|
||||
TargetEntry *tle= lfirst(x);
|
||||
result =
|
||||
lappend(result,
|
||||
MakeTLE(tle->resdom,
|
||||
flatten_tlistentry((Node*)get_expr(tle),
|
||||
flat_tlist)));
|
||||
}
|
||||
|
||||
return(result);
|
||||
}
|
||||
|
||||
/*
|
||||
* flatten-tlistentry--
|
||||
* Replaces vars within a target list entry with vars from a flattened
|
||||
* target list.
|
||||
*
|
||||
* 'tlistentry' is the target list entry to be modified
|
||||
* 'flat-tlist' is the flattened target list
|
||||
*
|
||||
* Returns the (modified) target_list entry from the target list.
|
||||
*
|
||||
*/
|
||||
static Node *
|
||||
flatten_tlistentry(Node *tlistentry, List *flat_tlist)
|
||||
{
|
||||
if (tlistentry==NULL) {
|
||||
|
||||
return NULL;
|
||||
|
||||
} else if (IsA (tlistentry,Var)) {
|
||||
|
||||
return
|
||||
((Node *)get_expr(match_varid((Var*)tlistentry,
|
||||
flat_tlist)));
|
||||
} else if (IsA (tlistentry,Iter)) {
|
||||
|
||||
((Iter*)tlistentry)->iterexpr =
|
||||
flatten_tlistentry((Node*)((Iter*)tlistentry)->iterexpr,
|
||||
flat_tlist);
|
||||
return tlistentry;
|
||||
|
||||
} else if (single_node(tlistentry)) {
|
||||
|
||||
return tlistentry;
|
||||
|
||||
} else if (is_funcclause (tlistentry)) {
|
||||
Expr *expr = (Expr*)tlistentry;
|
||||
List *temp_result = NIL;
|
||||
List *elt = NIL;
|
||||
|
||||
foreach(elt, expr->args)
|
||||
temp_result = lappend(temp_result,
|
||||
flatten_tlistentry(lfirst(elt),flat_tlist));
|
||||
|
||||
return
|
||||
((Node *)make_funcclause((Func*)expr->oper, temp_result));
|
||||
|
||||
} else if (IsA(tlistentry,Aggreg)) {
|
||||
|
||||
return tlistentry;
|
||||
|
||||
} else if (IsA(tlistentry,ArrayRef)) {
|
||||
ArrayRef *aref = (ArrayRef *)tlistentry;
|
||||
List *temp = NIL;
|
||||
List *elt = NIL;
|
||||
|
||||
foreach(elt, aref->refupperindexpr)
|
||||
temp = lappend(temp, flatten_tlistentry(lfirst(elt), flat_tlist));
|
||||
aref->refupperindexpr = temp;
|
||||
|
||||
temp = NIL;
|
||||
foreach(elt, aref->reflowerindexpr)
|
||||
temp = lappend(temp, flatten_tlistentry(lfirst(elt), flat_tlist));
|
||||
aref->reflowerindexpr = temp;
|
||||
|
||||
aref->refexpr =
|
||||
flatten_tlistentry(aref->refexpr, flat_tlist);
|
||||
|
||||
aref->refassgnexpr =
|
||||
flatten_tlistentry(aref->refassgnexpr, flat_tlist);
|
||||
|
||||
return tlistentry;
|
||||
} else {
|
||||
Expr *expr = (Expr*)tlistentry;
|
||||
Var *left =
|
||||
(Var*)flatten_tlistentry((Node*)get_leftop(expr),
|
||||
flat_tlist);
|
||||
Var *right =
|
||||
(Var*)flatten_tlistentry((Node*)get_rightop(expr),
|
||||
flat_tlist);
|
||||
|
||||
return((Node *)
|
||||
make_opclause((Oper*)expr->oper, left, right));
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
TargetEntry *
|
||||
MakeTLE(Resdom *resdom, Node *expr)
|
||||
{
|
||||
TargetEntry *rt = makeNode(TargetEntry);
|
||||
|
||||
rt->resdom = resdom;
|
||||
rt->expr = expr;
|
||||
return rt;
|
||||
}
|
||||
|
||||
Var *
|
||||
get_expr(TargetEntry *tle)
|
||||
{
|
||||
Assert(tle!=NULL);
|
||||
Assert(tle->expr!=NULL);
|
||||
|
||||
return ((Var *)tle->expr);
|
||||
}
|
||||
|
||||
|
||||
/*****************************************************************************
|
||||
*
|
||||
*****************************************************************************/
|
||||
|
||||
/*
|
||||
* AddGroupAttrToTlist -
|
||||
* append the group attribute to the target list if it's not already
|
||||
* in there.
|
||||
*/
|
||||
void
|
||||
AddGroupAttrToTlist(List *tlist, List *grpCl)
|
||||
{
|
||||
List *gl;
|
||||
int last_resdomno = length(tlist) + 1;
|
||||
|
||||
foreach (gl, grpCl) {
|
||||
GroupClause *gc = (GroupClause*)lfirst(gl);
|
||||
Var *var = gc->grpAttr;
|
||||
|
||||
if (!(tlist_member(var, tlist))) {
|
||||
Resdom *r;
|
||||
|
||||
r = makeResdom(last_resdomno,
|
||||
var->vartype,
|
||||
get_typlen(var->vartype),
|
||||
NULL,
|
||||
(Index)0,
|
||||
(Oid)0,
|
||||
0);
|
||||
last_resdomno++;
|
||||
tlist = lappend(tlist, MakeTLE(r, (Node*)var));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* was ExecTargetListLength() in execQual.c,
|
||||
moved here to reduce dependencies on the executor module */
|
||||
int
|
||||
exec_tlist_length(List *targetlist)
|
||||
{
|
||||
int len;
|
||||
List *tl;
|
||||
TargetEntry *curTle;
|
||||
|
||||
len = 0;
|
||||
foreach (tl, targetlist) {
|
||||
curTle = lfirst(tl);
|
||||
|
||||
if (curTle->resdom != NULL)
|
||||
len++;
|
||||
}
|
||||
return len;
|
||||
}
|
||||
|
||||
|
189
src/backend/optimizer/util/var.c
Normal file
189
src/backend/optimizer/util/var.c
Normal file
@@ -0,0 +1,189 @@
|
||||
/*-------------------------------------------------------------------------
|
||||
*
|
||||
* var.c--
|
||||
* Var node manipulation routines
|
||||
*
|
||||
* Copyright (c) 1994, Regents of the University of California
|
||||
*
|
||||
*
|
||||
* IDENTIFICATION
|
||||
* $Header: /cvsroot/pgsql/src/backend/optimizer/util/var.c,v 1.1.1.1 1996/07/09 06:21:39 scrappy Exp $
|
||||
*
|
||||
*-------------------------------------------------------------------------
|
||||
*/
|
||||
#include "nodes/primnodes.h"
|
||||
#include "nodes/nodeFuncs.h"
|
||||
|
||||
#include "optimizer/internal.h"
|
||||
#include "optimizer/clauses.h"
|
||||
#include "optimizer/var.h"
|
||||
|
||||
#include "parser/parsetree.h"
|
||||
|
||||
/*
|
||||
* find_varnos
|
||||
*
|
||||
* Descends down part of a parsetree (qual or tlist),
|
||||
*
|
||||
* XXX assumes varno's are always integers, which shouldn't be true...
|
||||
* (though it currently is, see primnodes.h)
|
||||
*/
|
||||
List *
|
||||
pull_varnos(Node *me)
|
||||
{
|
||||
List *i, *result = NIL;
|
||||
|
||||
if (me == NULL)
|
||||
return (NIL);
|
||||
|
||||
switch (nodeTag(me)) {
|
||||
case T_List:
|
||||
foreach (i, (List*)me) {
|
||||
result = nconc(result, pull_varnos(lfirst(i)));
|
||||
}
|
||||
break;
|
||||
case T_ArrayRef:
|
||||
foreach (i, ((ArrayRef*) me)->refupperindexpr)
|
||||
result = nconc(result, pull_varnos(lfirst(i)));
|
||||
foreach (i, ((ArrayRef*) me)->reflowerindexpr)
|
||||
result = nconc(result, pull_varnos(lfirst(i)));
|
||||
result = nconc(result, pull_varnos(((ArrayRef*) me)->refassgnexpr));
|
||||
break;
|
||||
case T_Var:
|
||||
result = lconsi(((Var*) me)->varno, NIL);
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
return(result);
|
||||
}
|
||||
|
||||
/*
|
||||
* contain_var_clause--
|
||||
* Recursively find var nodes from a clause by pulling vars from the
|
||||
* left and right operands of the clause.
|
||||
*
|
||||
* Returns true if any varnode found.
|
||||
*/
|
||||
bool contain_var_clause(Node *clause)
|
||||
{
|
||||
if (clause==NULL)
|
||||
return FALSE;
|
||||
else if (IsA(clause,Var))
|
||||
return TRUE;
|
||||
else if (IsA(clause,Iter))
|
||||
return contain_var_clause(((Iter*)clause)->iterexpr);
|
||||
else if (single_node(clause))
|
||||
return FALSE;
|
||||
else if (or_clause(clause)) {
|
||||
List *temp;
|
||||
|
||||
foreach (temp, ((Expr*)clause)->args) {
|
||||
if (contain_var_clause(lfirst(temp)))
|
||||
return TRUE;
|
||||
}
|
||||
return FALSE;
|
||||
} else if (is_funcclause (clause)) {
|
||||
List *temp;
|
||||
|
||||
foreach(temp, ((Expr *)clause)->args) {
|
||||
if (contain_var_clause(lfirst(temp)))
|
||||
return TRUE;
|
||||
}
|
||||
return FALSE;
|
||||
} else if (IsA(clause,ArrayRef)) {
|
||||
List *temp;
|
||||
|
||||
foreach(temp, ((ArrayRef*)clause)->refupperindexpr) {
|
||||
if (contain_var_clause(lfirst(temp)))
|
||||
return TRUE;
|
||||
}
|
||||
foreach(temp, ((ArrayRef*)clause)->reflowerindexpr) {
|
||||
if (contain_var_clause(lfirst(temp)))
|
||||
return TRUE;
|
||||
}
|
||||
if (contain_var_clause(((ArrayRef*)clause)->refexpr))
|
||||
return TRUE;
|
||||
if (contain_var_clause(((ArrayRef*)clause)->refassgnexpr))
|
||||
return TRUE;
|
||||
return FALSE;
|
||||
} else if (not_clause(clause))
|
||||
return contain_var_clause((Node*)get_notclausearg((Expr*)clause));
|
||||
else if (is_opclause(clause))
|
||||
return (contain_var_clause((Node*)get_leftop((Expr*)clause)) ||
|
||||
contain_var_clause((Node*)get_rightop((Expr*)clause)));
|
||||
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
/*
|
||||
* pull_var_clause--
|
||||
* Recursively pulls all var nodes from a clause by pulling vars from the
|
||||
* left and right operands of the clause.
|
||||
*
|
||||
* Returns list of varnodes found.
|
||||
*/
|
||||
List *
|
||||
pull_var_clause(Node *clause)
|
||||
{
|
||||
List *retval = NIL;
|
||||
|
||||
if (clause==NULL)
|
||||
return(NIL);
|
||||
else if (IsA(clause,Var))
|
||||
retval = lcons(clause,NIL);
|
||||
else if (IsA(clause,Iter))
|
||||
retval = pull_var_clause(((Iter*)clause)->iterexpr);
|
||||
else if (single_node(clause))
|
||||
retval = NIL;
|
||||
else if (or_clause(clause)) {
|
||||
List *temp;
|
||||
|
||||
foreach (temp, ((Expr*)clause)->args)
|
||||
retval = nconc(retval, pull_var_clause(lfirst(temp)));
|
||||
} else if (is_funcclause (clause)) {
|
||||
List *temp;
|
||||
|
||||
foreach(temp, ((Expr *)clause)->args)
|
||||
retval = nconc (retval,pull_var_clause(lfirst(temp)));
|
||||
} else if (IsA(clause,Aggreg)) {
|
||||
retval = pull_var_clause(((Aggreg*)clause)->target);
|
||||
} else if (IsA(clause,ArrayRef)) {
|
||||
List *temp;
|
||||
|
||||
foreach(temp, ((ArrayRef*)clause)->refupperindexpr)
|
||||
retval = nconc (retval,pull_var_clause(lfirst(temp)));
|
||||
foreach(temp, ((ArrayRef*)clause)->reflowerindexpr)
|
||||
retval = nconc (retval,pull_var_clause(lfirst(temp)));
|
||||
retval = nconc(retval,
|
||||
pull_var_clause(((ArrayRef*)clause)->refexpr));
|
||||
retval = nconc(retval,
|
||||
pull_var_clause(((ArrayRef*)clause)->refassgnexpr));
|
||||
} else if (not_clause(clause))
|
||||
retval = pull_var_clause((Node*)get_notclausearg((Expr*)clause));
|
||||
else if (is_opclause(clause))
|
||||
retval = nconc(pull_var_clause((Node*)get_leftop((Expr*)clause)),
|
||||
pull_var_clause((Node*)get_rightop((Expr*)clause)));
|
||||
else
|
||||
retval = NIL;
|
||||
|
||||
return (retval);
|
||||
}
|
||||
|
||||
/*
|
||||
* var_equal
|
||||
*
|
||||
* Returns t iff two var nodes correspond to the same attribute.
|
||||
*/
|
||||
bool
|
||||
var_equal(Var *var1, Var *var2)
|
||||
{
|
||||
if (IsA (var1,Var) && IsA (var2,Var) &&
|
||||
(((Var*)var1)->varno == ((Var*)var2)->varno) &&
|
||||
(((Var*)var1)->vartype == ((Var*)var2)->vartype) &&
|
||||
(((Var*)var1)->varattno == ((Var*)var2)->varattno)) {
|
||||
|
||||
return(true);
|
||||
} else
|
||||
return(false);
|
||||
}
|
21
src/backend/optimizer/var.h
Normal file
21
src/backend/optimizer/var.h
Normal file
@@ -0,0 +1,21 @@
|
||||
/*-------------------------------------------------------------------------
|
||||
*
|
||||
* var.h--
|
||||
* prototypes for var.c.
|
||||
*
|
||||
*
|
||||
* Copyright (c) 1994, Regents of the University of California
|
||||
*
|
||||
* $Id: var.h,v 1.1.1.1 1996/07/09 06:21:35 scrappy Exp $
|
||||
*
|
||||
*-------------------------------------------------------------------------
|
||||
*/
|
||||
#ifndef VAR_H
|
||||
#define VAR_H
|
||||
|
||||
extern List *pull_varnos(Node *me);
|
||||
extern bool contain_var_clause(Node *clause);
|
||||
extern List *pull_var_clause(Node *clause);
|
||||
extern bool var_equal(Var *var1, Var *var2);
|
||||
|
||||
#endif /* VAR_H */
|
84
src/backend/optimizer/xfunc.h
Normal file
84
src/backend/optimizer/xfunc.h
Normal file
@@ -0,0 +1,84 @@
|
||||
/*-------------------------------------------------------------------------
|
||||
*
|
||||
* xfunc.h--
|
||||
* prototypes for xfunc.c and predmig.c.
|
||||
*
|
||||
*
|
||||
* Copyright (c) 1994, Regents of the University of California
|
||||
*
|
||||
* $Id: xfunc.h,v 1.1.1.1 1996/07/09 06:21:35 scrappy Exp $
|
||||
*
|
||||
*-------------------------------------------------------------------------
|
||||
*/
|
||||
#ifndef XFUNC_H
|
||||
#define XFUNC_H
|
||||
|
||||
#include "nodes/relation.h"
|
||||
|
||||
/* command line arg flags */
|
||||
#define XFUNC_OFF -1 /* do no optimization of expensive preds */
|
||||
#define XFUNC_NOR 2 /* do no optimization of OR clauses */
|
||||
#define XFUNC_NOPULL 4 /* never pull restrictions above joins */
|
||||
#define XFUNC_NOPM 8 /* don't do predicate migration */
|
||||
#define XFUNC_WAIT 16 /* don't do pullup until predicate migration */
|
||||
#define XFUNC_PULLALL 32 /* pull all expensive restrictions up, always */
|
||||
|
||||
/* constants for local and join predicates */
|
||||
#define XFUNC_LOCPRD 1
|
||||
#define XFUNC_JOINPRD 2
|
||||
#define XFUNC_UNKNOWN 0
|
||||
|
||||
extern int XfuncMode; /* defined in tcop/postgres.c */
|
||||
|
||||
/* defaults for function attributes used for expensive function calculations */
|
||||
#define BYTE_PCT 100
|
||||
#define PERBYTE_CPU 0
|
||||
#define PERCALL_CPU 0
|
||||
#define OUTIN_RATIO 100
|
||||
|
||||
/* default width assumed for variable length attributes */
|
||||
#define VARLEN_DEFAULT 128;
|
||||
|
||||
/* Macro to get group rank out of group cost and group sel */
|
||||
#define get_grouprank(a) ((get_groupsel(a) - 1) / get_groupcost(a))
|
||||
|
||||
/* Macro to see if a path node is actually a Join */
|
||||
#define is_join(pathnode) (length(get_relids(get_parent(pathnode))) > 1 ? 1 : 0)
|
||||
|
||||
/* function prototypes from planner/path/xfunc.c */
|
||||
extern void xfunc_trypullup(Rel *rel);
|
||||
extern int xfunc_shouldpull(Path *childpath, JoinPath *parentpath,
|
||||
int whichchild, CInfo *maxcinfopt);
|
||||
extern CInfo *xfunc_pullup(Path *childpath, JoinPath *parentpath, CInfo *cinfo,
|
||||
int whichchild, int clausetype);
|
||||
extern Cost xfunc_rank(Expr *clause);
|
||||
extern Cost xfunc_expense(Query* queryInfo, Expr *clause);
|
||||
extern Cost xfunc_join_expense(JoinPath *path, int whichchild);
|
||||
extern Cost xfunc_local_expense(Expr *clause);
|
||||
extern Cost xfunc_func_expense(Expr *node, List *args);
|
||||
extern int xfunc_width(Expr *clause);
|
||||
/* static, moved to xfunc.c */
|
||||
/* extern int xfunc_card_unreferenced(Expr *clause, Relid referenced); */
|
||||
extern int xfunc_card_product(Relid relids);
|
||||
extern List *xfunc_find_references(List *clause);
|
||||
extern List *xfunc_primary_join(JoinPath *pathnode);
|
||||
extern Cost xfunc_get_path_cost(Path *pathnode);
|
||||
extern Cost xfunc_total_path_cost(JoinPath *pathnode);
|
||||
extern Cost xfunc_expense_per_tuple(JoinPath *joinnode, int whichchild);
|
||||
extern void xfunc_fixvars(Expr *clause, Rel *rel, int varno);
|
||||
extern int xfunc_cinfo_compare(void *arg1, void *arg2);
|
||||
extern int xfunc_clause_compare(void *arg1, void *arg2);
|
||||
extern void xfunc_disjunct_sort(List *clause_list);
|
||||
extern int xfunc_disjunct_compare(void *arg1, void *arg2);
|
||||
extern int xfunc_func_width(RegProcedure funcid, List *args);
|
||||
extern int xfunc_tuple_width(Relation rd);
|
||||
extern int xfunc_num_join_clauses(JoinPath *path);
|
||||
extern List *xfunc_LispRemove(List *foo, List *bar);
|
||||
extern bool xfunc_copyrel(Rel *from, Rel **to);
|
||||
|
||||
/*
|
||||
* function prototypes for path/predmig.c
|
||||
*/
|
||||
extern bool xfunc_do_predmig(Path root);
|
||||
|
||||
#endif /* XFUNC_H */
|
Reference in New Issue
Block a user