mirror of
https://github.com/postgres/postgres.git
synced 2025-11-09 06:21:09 +03:00
Postgres95 1.01 Distribution - Virgin Sources
This commit is contained in:
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);
|
||||
}
|
||||
Reference in New Issue
Block a user