1
0
mirror of https://github.com/postgres/postgres.git synced 2025-05-03 22:24:49 +03:00
Tom Lane 9e7e29e6c9 First cut at doing LIKE/regex indexing optimization in
optimizer rather than parser.  This has many advantages, such as not
getting fooled by chance uses of operator names ~ and ~~ (the operators
are identified by OID now), and not creating useless comparison operations
in contexts where the comparisons will not actually be used as indexquals.
The new code also recognizes exact-match LIKE and regex patterns, and
produces an = indexqual instead of >= and <=.

This change does NOT fix the problem with non-ASCII locales: the code
still doesn't know how to generate an upper bound indexqual for non-ASCII
collation order.  But it's no worse than before, just the same deficiency
in a different place...

Also, dike out loc_restrictinfo fields in Plan nodes.  These were doing
nothing useful in the absence of 'expensive functions' optimization,
and they took a considerable amount of processing to fill in.
1999-07-27 03:51:11 +00:00

953 lines
23 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.43 1999/07/27 03:51:04 tgl 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/makefuncs.h"
#include "nodes/nodeFuncs.h"
#include "nodes/plannodes.h"
#include "optimizer/clauses.h"
#include "optimizer/internal.h"
#include "optimizer/var.h"
#include "utils/lsyscache.h"
static bool fix_opid_walker(Node *node, void *context);
static int is_single_func(Node *node);
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;
}
/*****************************************************************************
* 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;
}
/*
* Sometimes (such as in the result of cnfify), we use lists of expression
* nodes with implicit AND semantics. These functions convert between an
* AND-semantics expression list and the ordinary representation of a
* boolean expression.
*/
Expr *
make_ands_explicit(List *andclauses)
{
if (andclauses == NIL)
return NULL;
else if (length(andclauses) == 1)
return (Expr *) lfirst(andclauses);
else
return make_andclause(andclauses);
}
List *
make_ands_implicit(Expr *clause)
{
if (clause == NULL)
return NIL;
else if (and_clause((Node *) clause))
return clause->args;
else
return lcons(clause, NIL);
}
/*****************************************************************************
* 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;
foreach(i, clvars)
{
Var *var = (Var *) lfirst(i);
List *vi;
Assert(var->varlevelsup == 0);
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;
}
/*
* NumRelids
* (formerly clause_relids)
*
* Returns the number of different relations referenced in 'clause'.
*/
int
NumRelids(Node *clause)
{
List *vars = pull_var_clause(clause);
List *var_list = NIL;
List *i;
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);
}
/*
* 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 opid field from opno for each Oper node in given tree.
*
* Returns nothing.
*/
void
fix_opid(Node *clause)
{
/* This tree walk requires no special setup, so away we go... */
fix_opid_walker(clause, NULL);
}
static bool
fix_opid_walker (Node *node, void *context)
{
if (node == NULL)
return false;
if (is_opclause(node))
replace_opid((Oper *) ((Expr *) node)->oper);
return expression_tree_walker(node, fix_opid_walker, context);
}
/*
* fix_opids
* Calculate the opid from the opno for all the clauses...
*
* Returns its argument.
*
* XXX This could and should be merged with fix_opid.
*
*/
List *
fix_opids(List *clauses)
{
fix_opid((Node *) clauses);
return clauses;
}
/*
* get_relattval
* Extract information from a restriction or join clause for
* selectivity estimation. The inputs are an expression
* and a relation number (which can be 0 if we don't care which
* relation is used; that'd normally be the case for restriction
* clauses, where the caller already knows that only one relation
* is referenced in the clause). The routine checks that the
* expression is of the form (var op something) or (something op var)
* where the var is an attribute of the specified relation, or
* a function of a var of the specified relation. If so, it
* returns the following info:
* the found relation number (same as targetrelid unless that is 0)
* the found var number (or InvalidAttrNumber if a function)
* if the "something" is a constant, the value of the constant
* flags indicating whether a constant was found, and on which side.
* Default values are returned if the expression is too complicated,
* specifically -1 for the relid and attno, 0 for the constant value.
* Note that InvalidAttrNumber is *not* -1, but 0.
*/
void
get_relattval(Node *clause,
int targetrelid,
int *relid,
AttrNumber *attno,
Datum *constval,
int *flag)
{
Var *left,
*right,
*other;
int funcvarno;
/* 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;
/* First look for the var or func */
if (IsA(left, Var) &&
(targetrelid == 0 || targetrelid == left->varno))
{
*relid = left->varno;
*attno = left->varattno;
*flag = SEL_RIGHT;
}
else if (IsA(right, Var) &&
(targetrelid == 0 || targetrelid == right->varno))
{
*relid = right->varno;
*attno = right->varattno;
*flag = 0;
}
else if ((funcvarno = is_single_func((Node *) left)) != 0 &&
(targetrelid == 0 || targetrelid == funcvarno))
{
*relid = funcvarno;
*attno = InvalidAttrNumber;
*flag = SEL_RIGHT;
}
else if ((funcvarno = is_single_func((Node *) right)) != 0 &&
(targetrelid == 0 || targetrelid == funcvarno))
{
*relid = funcvarno;
*attno = InvalidAttrNumber;
*flag = 0;
}
else
{
/* Duh, it's too complicated for me... */
default_results:
*relid = -1;
*attno = -1;
*constval = 0;
*flag = 0;
return;
}
/* OK, we identified the var or func; now look at the other side */
other = (*flag == 0) ? left : right;
if (IsA(other, Const))
{
*constval = ((Const *) other)->constvalue;
*flag |= SEL_CONSTANT;
}
else
{
*constval = 0;
}
}
/*
* is_single_func
* If the given expression is a function of a single relation,
* return the relation number; else return 0
*/
static int is_single_func(Node *node)
{
if (is_funcclause(node))
{
List *vars = pull_var_clause(node);
if (vars != NIL)
{
int funcvarno = ((Var *) lfirst(vars))->varno;
/* need to check that all args of func are same relation */
while ((vars = lnext(vars)) != NIL)
{
if (((Var *) lfirst(vars))->varno != funcvarno)
return 0;
}
return funcvarno;
}
}
return 0;
}
/*
* get_rels_atts
*
* Returns the info
* ( relid1 attno1 relid2 attno2 )
* for a joinclause.
*
* If the clause is not of the form (var op 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)
{
/* set default values */
*relid1 = -1;
*attno1 = -1;
*relid2 = -1;
*attno2 = -1;
if (is_opclause(clause))
{
Var *left = get_leftop((Expr *) clause);
Var *right = get_rightop((Expr *) clause);
if (left && right)
{
int funcvarno;
if (IsA(left, Var))
{
*relid1 = left->varno;
*attno1 = left->varattno;
}
else if ((funcvarno = is_single_func((Node *) left)) != 0)
{
*relid1 = funcvarno;
*attno1 = InvalidAttrNumber;
}
if (IsA(right, Var))
{
*relid2 = right->varno;
*attno2 = right->varattno;
}
else if ((funcvarno = is_single_func((Node *) right)) != 0)
{
*relid2 = funcvarno;
*attno2 = InvalidAttrNumber;
}
}
}
}
/*--------------------
* CommuteClause: commute a binary operator clause
*--------------------
*/
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;
}
/*--------------------
* Standard expression-tree walking support
*
* We used to have near-duplicate code in many different routines that
* understood how to recurse through an expression node tree. That was
* a pain to maintain, and we frequently had bugs due to some particular
* routine neglecting to support a particular node type. In most cases,
* these routines only actually care about certain node types, and don't
* care about other types except insofar as they have to recurse through
* non-primitive node types. Therefore, we now provide generic tree-walking
* logic to consolidate the redundant "boilerplate" code.
*
* expression_tree_walker() is designed to support routines that traverse
* a tree in a read-only fashion (although it will also work for routines
* that modify nodes in-place but never add or delete nodes). A walker
* routine should look like this:
*
* bool my_walker (Node *node, my_struct *context)
* {
* if (node == NULL)
* return false;
* // check for nodes that special work is required for, eg:
* if (IsA(node, Var))
* {
* ... do special actions for Var nodes
* }
* else if (IsA(node, ...))
* {
* ... do special actions for other node types
* }
* // for any node type not specially processed, do:
* return expression_tree_walker(node, my_walker, (void *) context);
* }
*
* The "context" argument points to a struct that holds whatever context
* information the walker routine needs (it can be used to return data
* gathered by the walker, too). This argument is not touched by
* expression_tree_walker, but it is passed down to recursive sub-invocations
* of my_walker. The tree walk is started from a setup routine that
* fills in the appropriate context struct, calls my_walker with the top-level
* node of the tree, and then examines the results.
*
* The walker routine should return "false" to continue the tree walk, or
* "true" to abort the walk and immediately return "true" to the top-level
* caller. This can be used to short-circuit the traversal if the walker
* has found what it came for. "false" is returned to the top-level caller
* iff no invocation of the walker returned "true".
*
* The node types handled by expression_tree_walker include all those
* normally found in target lists and qualifier clauses during the planning
* stage. In particular, it handles List nodes since a cnf-ified qual clause
* will have List structure at the top level, and it handles TargetEntry nodes
* so that a scan of a target list can be handled without additional code.
* (But only the "expr" part of a TargetEntry is examined, unless the walker
* chooses to process TargetEntry nodes specially.)
*
* expression_tree_walker will handle a SUBPLAN_EXPR node by recursing into
* the args and slink->oper lists (which belong to the outer plan), but it
* will *not* visit the inner plan, since that's typically what expression
* tree walkers want. A walker that wants to visit the subplan can force
* appropriate behavior by recognizing subplan nodes and doing the right
* thing.
*
* Bare SubLink nodes (without a SUBPLAN_EXPR) are handled by recursing into
* the "lefthand" argument list only. (A bare SubLink should be seen only if
* the tree has not yet been processed by subselect.c.) Again, this can be
* overridden by the walker, but it seems to be the most useful default
* behavior.
*--------------------
*/
bool
expression_tree_walker(Node *node, bool (*walker) (), void *context)
{
List *temp;
/*
* The walker has already visited the current node,
* and so we need only recurse into any sub-nodes it has.
*
* We assume that the walker is not interested in List nodes per se,
* so when we expect a List we just recurse directly to self without
* bothering to call the walker.
*/
if (node == NULL)
return false;
switch (nodeTag(node))
{
case T_Ident:
case T_Const:
case T_Var:
case T_Param:
/* primitive node types with no subnodes */
break;
case T_Expr:
{
Expr *expr = (Expr *) node;
if (expr->opType == SUBPLAN_EXPR)
{
/* examine args list (params to be passed to subplan) */
if (expression_tree_walker((Node *) expr->args,
walker, context))
return true;
/* examine oper list as well */
if (expression_tree_walker(
(Node *) ((SubPlan *) expr->oper)->sublink->oper,
walker, context))
return true;
/* but not the subplan itself */
}
else
{
/* for other Expr node types, just examine args list */
if (expression_tree_walker((Node *) expr->args,
walker, context))
return true;
}
}
break;
case T_Aggref:
return walker(((Aggref *) node)->target, context);
case T_Iter:
return walker(((Iter *) node)->iterexpr, context);
case T_ArrayRef:
{
ArrayRef *aref = (ArrayRef *) node;
/* recurse directly for upper/lower array index lists */
if (expression_tree_walker((Node *) aref->refupperindexpr,
walker, context))
return true;
if (expression_tree_walker((Node *) aref->reflowerindexpr,
walker, context))
return true;
/* walker must see the refexpr and refassgnexpr, however */
if (walker(aref->refexpr, context))
return true;
if (walker(aref->refassgnexpr, context))
return true;
}
break;
case T_CaseExpr:
{
CaseExpr *caseexpr = (CaseExpr *) node;
/* we assume walker doesn't care about CaseWhens, either */
foreach(temp, caseexpr->args)
{
CaseWhen *when = (CaseWhen *) lfirst(temp);
Assert(IsA(when, CaseWhen));
if (walker(when->expr, context))
return true;
if (walker(when->result, context))
return true;
}
/* caseexpr->arg should be null, but we'll check it anyway */
if (walker(caseexpr->arg, context))
return true;
if (walker(caseexpr->defresult, context))
return true;
}
break;
case T_SubLink:
/* A "bare" SubLink (note we will not come here if we found
* a SUBPLAN_EXPR node above). Examine the lefthand side,
* but not the oper list nor the subquery.
*/
return walker(((SubLink *) node)->lefthand, context);
case T_List:
foreach(temp, (List *) node)
{
if (walker((Node *) lfirst(temp), context))
return true;
}
break;
case T_TargetEntry:
return walker(((TargetEntry *) node)->expr, context);
default:
elog(ERROR, "expression_tree_walker: Unexpected node type %d",
nodeTag(node));
break;
}
return false;
}