/*------------------------------------------------------------------------- * * 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.32 1999/03/01 00:10:35 tgl Exp $ * * HISTORY * AUTHOR DATE MAJOR EVENT * Andrew Yu Nov 3, 1994 clause.c and clauses.c combined * *------------------------------------------------------------------------- */ #include "postgres.h" #include #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 %d", ((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; }