1
0
mirror of https://github.com/postgres/postgres.git synced 2025-05-28 05:21:27 +03:00
1999-05-25 16:15:34 +00:00

808 lines
18 KiB
C

/*-------------------------------------------------------------------------
*
* 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.34 1999/05/25 16:09:50 momjian Exp $
*
* HISTORY
* AUTHOR DATE MAJOR EVENT
* Andrew Yu Nov 3, 1994 clause.c and clauses.c combined
*
*-------------------------------------------------------------------------
*/
#include "postgres.h"
#include <catalog/pg_operator.h>
#include "nodes/primnodes.h"
#include "nodes/relation.h"
#include "nodes/parsenodes.h"
#include "nodes/plannodes.h"
#include "nodes/makefuncs.h"
#include "nodes/nodeFuncs.h"
#include "catalog/pg_aggregate.h"
#include "utils/syscache.h"
#include "utils/lsyscache.h"
#include "optimizer/clauses.h"
#include "optimizer/internal.h"
#include "optimizer/var.h"
static bool agg_clause(Node *clause);
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
{
elog(ERROR, "make_clause: unsupported type %d", type);
/* 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;
if (rightop)
expr->args = lcons(leftop, lcons(rightop, NIL));
else
expr->args = lcons(leftop, NIL);
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).
* NB: result will be NULL if applied to a unary op clause.
*/
Var *
get_rightop(Expr *clause)
{
if (clause->args != NULL && lnext(clause->args) != NULL)
return lfirst(lnext(clause->args));
else
return NULL;
}
/*****************************************************************************
* AGG clause functions
*****************************************************************************/
static bool
agg_clause(Node *clause)
{
return (clause != NULL && nodeTag(clause) == T_Aggref);
}
/*****************************************************************************
* 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;
}
/*****************************************************************************
* CASE clause functions
*****************************************************************************/
/*
* case_clause
*
* Returns t iff its argument is a 'case' clause: (CASE { expr }).
*
*/
bool
case_clause(Node *clause)
{
return (clause != NULL &&
nodeTag(clause) == T_CaseExpr);
}
/*****************************************************************************
* *
* *
* *
*****************************************************************************/
/*
* 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.
*
*/
void
clause_get_relids_vars(Node *clause, Relids * 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);
List *vi;
if (!intMember(var->varno, varno_list))
varno_list = lappendi(varno_list, var->varno);
foreach(vi, var_list)
{
Var *in_list = (Var *) lfirst(vi);
Assert(var->varlevelsup == 0);
if (in_list->varno == var->varno &&
in_list->varattno == var->varattno)
break;
}
if (vi == NIL)
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) || and_clause(clause))
{
List *a;
foreach(a, ((Expr *) clause)->args)
{
if (contains_not(lfirst(a)))
return true;
}
}
return false;
}
/*
* is_joinable
*
* Returns t iff 'clause' is a valid join clause.
*
*/
bool
is_joinable(Node *clause)
{
Node *leftop,
*rightop;
if (!is_opclause(clause))
return false;
leftop = (Node *) get_leftop((Expr *) clause);
rightop = (Node *) get_rightop((Expr *) clause);
if (!rightop)
return false; /* unary opclauses need not apply */
/*
* 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.
*
* For now we accept only "var op const" or "const op var".
*/
bool
qual_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);
if (!rightop)
return false; /* unary opclauses need not apply */
/* How about Param-s ? - vadim 02/03/98 */
if (IsA(leftop, Var) &&IsA(rightop, Const))
return true;
if (IsA(rightop, Var) &&IsA(leftop, 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) || and_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));
}
else if (agg_clause(clause))
fix_opid(((Aggref *) clause)->target);
else if (is_subplan(clause) &&
((SubPlan *) ((Expr *) clause)->oper)->sublink->subLinkType != EXISTS_SUBLINK)
{
List *lst;
foreach(lst, ((SubPlan *) ((Expr *) clause)->oper)->sublink->oper)
{
replace_opid((Oper *) ((Expr *) lfirst(lst))->oper);
fix_opid((Node *) get_leftop((Expr *) lfirst(lst)));
}
}
else if (case_clause(clause))
{
List *lst;
fix_opid(((CaseExpr *) clause)->defresult);
/* Run through the WHEN clauses... */
foreach(lst, ((CaseExpr *) clause)->args)
{
fix_opid(((CaseWhen *) lfirst(lst))->expr);
fix_opid(((CaseWhen *) lfirst(lst))->result);
}
}
}
/*
* 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 attributes 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 caused '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,
*right;
/* Careful; the passed clause might not be a binary operator at all */
if (!is_opclause(clause))
goto default_results;
left = get_leftop((Expr *) clause);
right = get_rightop((Expr *) clause);
if (!right)
goto default_results;
if (IsA(left, Var) &&IsA(right, Const))
{
*relid = left->varno;
*attno = left->varattno;
*constval = ((Const *) right)->constvalue;
*flag = (_SELEC_CONSTANT_RIGHT_ | _SELEC_IS_CONSTANT_);
}
else if (IsA(left, Var) &&IsA(right, Param))
{
*relid = left->varno;
*attno = left->varattno;
*constval = 0;
*flag = (_SELEC_NOT_CONSTANT_);
}
else if (is_funcclause((Node *) left) && IsA(right, Const))
{
List *vars = pull_var_clause((Node *) left);
*relid = ((Var *) lfirst(vars))->varno;
*attno = InvalidAttrNumber;
*constval = ((Const *) right)->constvalue;
*flag = (_SELEC_CONSTANT_RIGHT_ | _SELEC_IS_CONSTANT_);
}
else if (IsA(right, Var) &&IsA(left, Const))
{
*relid = right->varno;
*attno = right->varattno;
*constval = ((Const *) left)->constvalue;
*flag = (_SELEC_IS_CONSTANT_);
}
else if (IsA(right, Var) &&IsA(left, Param))
{
*relid = right->varno;
*attno = right->varattno;
*constval = 0;
*flag = (_SELEC_NOT_CONSTANT_);
}
else if (is_funcclause((Node *) right) && IsA(left, Const))
{
List *vars = pull_var_clause((Node *) right);
*relid = ((Var *) lfirst(vars))->varno;
*attno = InvalidAttrNumber;
*constval = ((Const *) left)->constvalue;
*flag = (_SELEC_IS_CONSTANT_);
}
else
{
/* Duh, it's too complicated for me... */
default_results:
*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)
{
if (is_opclause(clause))
{
Var *left = get_leftop((Expr *) clause);
Var *right = get_rightop((Expr *) clause);
if (left && right)
{
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 (var_left && var_right)
{
*relid1 = left->varno;
*attno1 = left->varoattno;
*relid2 = right->varno;
*attno2 = right->varoattno;
return;
}
if (var_left && varexpr_right)
{
*relid1 = left->varno;
*attno1 = left->varoattno;
*relid2 = _SELEC_VALUE_UNKNOWN_;
*attno2 = _SELEC_VALUE_UNKNOWN_;
return;
}
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_;
}
void
CommuteClause(Node *clause)
{
Node *temp;
Oper *commu;
Form_pg_operator commuTup;
HeapTuple heapTup;
if (!is_opclause(clause))
elog(ERROR, "CommuteClause: applied to non-operator clause");
heapTup = (HeapTuple)
get_operator_tuple(get_commutator(((Oper *) ((Expr *) clause)->oper)->opno));
if (heapTup == (HeapTuple) NULL)
elog(ERROR, "CommuteClause: no commutator for operator %u",
((Oper *) ((Expr *) clause)->oper)->opno);
commuTup = (Form_pg_operator) GETSTRUCT(heapTup);
commu = makeOper(heapTup->t_data->t_oid,
commuTup->oprcode,
commuTup->oprresult,
((Oper *) ((Expr *) clause)->oper)->opsize,
NULL);
/*
* re-form the clause in-place!
*/
((Expr *) clause)->oper = (Node *) commu;
temp = lfirst(((Expr *) clause)->args);
lfirst(((Expr *) clause)->args) = lsecond(((Expr *) clause)->args);
lsecond(((Expr *) clause)->args) = temp;
}