/*------------------------------------------------------------------------- * * 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.2 1996/07/25 20:36:46 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); 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); 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)) { 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; }