mirror of
https://github.com/postgres/postgres.git
synced 2025-11-09 06:21:09 +03:00
In the past, we used a 'Lispy' linked list implementation: a "list" was merely a pointer to the head node of the list. The problem with that design is that it makes lappend() and length() linear time. This patch fixes that problem (and others) by maintaining a count of the list length and a pointer to the tail node along with each head node pointer. A "list" is now a pointer to a structure containing some meta-data about the list; the head and tail pointers in that structure refer to ListCell structures that maintain the actual linked list of nodes. The function names of the list API have also been changed to, I hope, be more logically consistent. By default, the old function names are still available; they will be disabled-by-default once the rest of the tree has been updated to use the new API names.
623 lines
16 KiB
C
623 lines
16 KiB
C
/*-------------------------------------------------------------------------
|
|
*
|
|
* var.c
|
|
* Var node manipulation routines
|
|
*
|
|
* Portions Copyright (c) 1996-2003, PostgreSQL Global Development Group
|
|
* Portions Copyright (c) 1994, Regents of the University of California
|
|
*
|
|
*
|
|
* IDENTIFICATION
|
|
* $PostgreSQL: pgsql/src/backend/optimizer/util/var.c,v 1.57 2004/05/26 04:41:27 neilc Exp $
|
|
*
|
|
*-------------------------------------------------------------------------
|
|
*/
|
|
#include "postgres.h"
|
|
|
|
#include "nodes/plannodes.h"
|
|
#include "optimizer/clauses.h"
|
|
#include "optimizer/prep.h"
|
|
#include "optimizer/var.h"
|
|
#include "parser/parsetree.h"
|
|
#include "rewrite/rewriteManip.h"
|
|
|
|
|
|
typedef struct
|
|
{
|
|
Relids varnos;
|
|
int sublevels_up;
|
|
} pull_varnos_context;
|
|
|
|
typedef struct
|
|
{
|
|
int varno;
|
|
int varattno;
|
|
int sublevels_up;
|
|
} contain_var_reference_context;
|
|
|
|
typedef struct
|
|
{
|
|
int min_varlevel;
|
|
int sublevels_up;
|
|
} find_minimum_var_level_context;
|
|
|
|
typedef struct
|
|
{
|
|
FastList varlist;
|
|
bool includeUpperVars;
|
|
} pull_var_clause_context;
|
|
|
|
typedef struct
|
|
{
|
|
Query *root;
|
|
int sublevels_up;
|
|
} flatten_join_alias_vars_context;
|
|
|
|
static bool pull_varnos_walker(Node *node,
|
|
pull_varnos_context *context);
|
|
static bool contain_var_reference_walker(Node *node,
|
|
contain_var_reference_context *context);
|
|
static bool contain_var_clause_walker(Node *node, void *context);
|
|
static bool contain_vars_of_level_walker(Node *node, int *sublevels_up);
|
|
static bool contain_vars_above_level_walker(Node *node, int *sublevels_up);
|
|
static bool find_minimum_var_level_walker(Node *node,
|
|
find_minimum_var_level_context *context);
|
|
static bool pull_var_clause_walker(Node *node,
|
|
pull_var_clause_context *context);
|
|
static Node *flatten_join_alias_vars_mutator(Node *node,
|
|
flatten_join_alias_vars_context *context);
|
|
static Relids alias_relid_set(Query *root, Relids relids);
|
|
|
|
|
|
/*
|
|
* pull_varnos
|
|
* Create a set of all the distinct varnos present in a parsetree.
|
|
* Only varnos that reference level-zero rtable entries are considered.
|
|
*
|
|
* NOTE: this is used on not-yet-planned expressions. It may therefore find
|
|
* bare SubLinks, and if so it needs to recurse into them to look for uplevel
|
|
* references to the desired rtable level! But when we find a completed
|
|
* SubPlan, we only need to look at the parameters passed to the subplan.
|
|
*/
|
|
Relids
|
|
pull_varnos(Node *node)
|
|
{
|
|
pull_varnos_context context;
|
|
|
|
context.varnos = NULL;
|
|
context.sublevels_up = 0;
|
|
|
|
/*
|
|
* Must be prepared to start with a Query or a bare expression tree;
|
|
* if it's a Query, we don't want to increment sublevels_up.
|
|
*/
|
|
query_or_expression_tree_walker(node,
|
|
pull_varnos_walker,
|
|
(void *) &context,
|
|
0);
|
|
|
|
return context.varnos;
|
|
}
|
|
|
|
static bool
|
|
pull_varnos_walker(Node *node, pull_varnos_context *context)
|
|
{
|
|
if (node == NULL)
|
|
return false;
|
|
if (IsA(node, Var))
|
|
{
|
|
Var *var = (Var *) node;
|
|
|
|
if (var->varlevelsup == context->sublevels_up)
|
|
context->varnos = bms_add_member(context->varnos, var->varno);
|
|
return false;
|
|
}
|
|
if (IsA(node, Query))
|
|
{
|
|
/* Recurse into RTE subquery or not-yet-planned sublink subquery */
|
|
bool result;
|
|
|
|
context->sublevels_up++;
|
|
result = query_tree_walker((Query *) node, pull_varnos_walker,
|
|
(void *) context, 0);
|
|
context->sublevels_up--;
|
|
return result;
|
|
}
|
|
return expression_tree_walker(node, pull_varnos_walker,
|
|
(void *) context);
|
|
}
|
|
|
|
|
|
/*
|
|
* contain_var_reference
|
|
*
|
|
* Detect whether a parsetree contains any references to a specified
|
|
* attribute of a specified rtable entry.
|
|
*
|
|
* NOTE: this is used on not-yet-planned expressions. It may therefore find
|
|
* bare SubLinks, and if so it needs to recurse into them to look for uplevel
|
|
* references to the desired rtable entry! But when we find a completed
|
|
* SubPlan, we only need to look at the parameters passed to the subplan.
|
|
*/
|
|
bool
|
|
contain_var_reference(Node *node, int varno, int varattno, int levelsup)
|
|
{
|
|
contain_var_reference_context context;
|
|
|
|
context.varno = varno;
|
|
context.varattno = varattno;
|
|
context.sublevels_up = levelsup;
|
|
|
|
/*
|
|
* Must be prepared to start with a Query or a bare expression tree;
|
|
* if it's a Query, we don't want to increment sublevels_up.
|
|
*/
|
|
return query_or_expression_tree_walker(node,
|
|
contain_var_reference_walker,
|
|
(void *) &context,
|
|
0);
|
|
}
|
|
|
|
static bool
|
|
contain_var_reference_walker(Node *node,
|
|
contain_var_reference_context *context)
|
|
{
|
|
if (node == NULL)
|
|
return false;
|
|
if (IsA(node, Var))
|
|
{
|
|
Var *var = (Var *) node;
|
|
|
|
if (var->varno == context->varno &&
|
|
var->varattno == context->varattno &&
|
|
var->varlevelsup == context->sublevels_up)
|
|
return true;
|
|
return false;
|
|
}
|
|
if (IsA(node, Query))
|
|
{
|
|
/* Recurse into RTE subquery or not-yet-planned sublink subquery */
|
|
bool result;
|
|
|
|
context->sublevels_up++;
|
|
result = query_tree_walker((Query *) node,
|
|
contain_var_reference_walker,
|
|
(void *) context, 0);
|
|
context->sublevels_up--;
|
|
return result;
|
|
}
|
|
return expression_tree_walker(node, contain_var_reference_walker,
|
|
(void *) context);
|
|
}
|
|
|
|
|
|
/*
|
|
* contain_var_clause
|
|
* Recursively scan a clause to discover whether it contains any Var nodes
|
|
* (of the current query level).
|
|
*
|
|
* Returns true if any varnode found.
|
|
*
|
|
* Does not examine subqueries, therefore must only be used after reduction
|
|
* of sublinks to subplans!
|
|
*/
|
|
bool
|
|
contain_var_clause(Node *node)
|
|
{
|
|
return contain_var_clause_walker(node, NULL);
|
|
}
|
|
|
|
static bool
|
|
contain_var_clause_walker(Node *node, void *context)
|
|
{
|
|
if (node == NULL)
|
|
return false;
|
|
if (IsA(node, Var))
|
|
{
|
|
if (((Var *) node)->varlevelsup == 0)
|
|
return true; /* abort the tree traversal and return
|
|
* true */
|
|
return false;
|
|
}
|
|
return expression_tree_walker(node, contain_var_clause_walker, context);
|
|
}
|
|
|
|
/*
|
|
* contain_vars_of_level
|
|
* Recursively scan a clause to discover whether it contains any Var nodes
|
|
* of the specified query level.
|
|
*
|
|
* Returns true if any such Var found.
|
|
*
|
|
* Will recurse into sublinks. Also, may be invoked directly on a Query.
|
|
*/
|
|
bool
|
|
contain_vars_of_level(Node *node, int levelsup)
|
|
{
|
|
int sublevels_up = levelsup;
|
|
|
|
return query_or_expression_tree_walker(node,
|
|
contain_vars_of_level_walker,
|
|
(void *) &sublevels_up,
|
|
0);
|
|
}
|
|
|
|
static bool
|
|
contain_vars_of_level_walker(Node *node, int *sublevels_up)
|
|
{
|
|
if (node == NULL)
|
|
return false;
|
|
if (IsA(node, Var))
|
|
{
|
|
if (((Var *) node)->varlevelsup == *sublevels_up)
|
|
return true; /* abort tree traversal and return true */
|
|
}
|
|
if (IsA(node, Query))
|
|
{
|
|
/* Recurse into subselects */
|
|
bool result;
|
|
|
|
(*sublevels_up)++;
|
|
result = query_tree_walker((Query *) node,
|
|
contain_vars_of_level_walker,
|
|
(void *) sublevels_up,
|
|
0);
|
|
(*sublevels_up)--;
|
|
return result;
|
|
}
|
|
return expression_tree_walker(node,
|
|
contain_vars_of_level_walker,
|
|
(void *) sublevels_up);
|
|
}
|
|
|
|
/*
|
|
* contain_vars_above_level
|
|
* Recursively scan a clause to discover whether it contains any Var nodes
|
|
* above the specified query level. (For example, pass zero to detect
|
|
* all nonlocal Vars.)
|
|
*
|
|
* Returns true if any such Var found.
|
|
*
|
|
* Will recurse into sublinks. Also, may be invoked directly on a Query.
|
|
*/
|
|
bool
|
|
contain_vars_above_level(Node *node, int levelsup)
|
|
{
|
|
int sublevels_up = levelsup;
|
|
|
|
return query_or_expression_tree_walker(node,
|
|
contain_vars_above_level_walker,
|
|
(void *) &sublevels_up,
|
|
0);
|
|
}
|
|
|
|
static bool
|
|
contain_vars_above_level_walker(Node *node, int *sublevels_up)
|
|
{
|
|
if (node == NULL)
|
|
return false;
|
|
if (IsA(node, Var))
|
|
{
|
|
if (((Var *) node)->varlevelsup > *sublevels_up)
|
|
return true; /* abort tree traversal and return true */
|
|
}
|
|
if (IsA(node, Query))
|
|
{
|
|
/* Recurse into subselects */
|
|
bool result;
|
|
|
|
(*sublevels_up)++;
|
|
result = query_tree_walker((Query *) node,
|
|
contain_vars_above_level_walker,
|
|
(void *) sublevels_up,
|
|
0);
|
|
(*sublevels_up)--;
|
|
return result;
|
|
}
|
|
return expression_tree_walker(node,
|
|
contain_vars_above_level_walker,
|
|
(void *) sublevels_up);
|
|
}
|
|
|
|
|
|
/*
|
|
* find_minimum_var_level
|
|
* Recursively scan a clause to find the lowest variable level it
|
|
* contains --- for example, zero is returned if there are any local
|
|
* variables, one if there are no local variables but there are
|
|
* one-level-up outer references, etc. Subqueries are scanned to see
|
|
* if they possess relevant outer references. (But any local variables
|
|
* within subqueries are not relevant.)
|
|
*
|
|
* -1 is returned if the clause has no variables at all.
|
|
*
|
|
* Will recurse into sublinks. Also, may be invoked directly on a Query.
|
|
*/
|
|
int
|
|
find_minimum_var_level(Node *node)
|
|
{
|
|
find_minimum_var_level_context context;
|
|
|
|
context.min_varlevel = -1; /* signifies nothing found yet */
|
|
context.sublevels_up = 0;
|
|
|
|
(void) query_or_expression_tree_walker(node,
|
|
find_minimum_var_level_walker,
|
|
(void *) &context,
|
|
0);
|
|
|
|
return context.min_varlevel;
|
|
}
|
|
|
|
static bool
|
|
find_minimum_var_level_walker(Node *node,
|
|
find_minimum_var_level_context *context)
|
|
{
|
|
if (node == NULL)
|
|
return false;
|
|
if (IsA(node, Var))
|
|
{
|
|
int varlevelsup = ((Var *) node)->varlevelsup;
|
|
|
|
/* convert levelsup to frame of reference of original query */
|
|
varlevelsup -= context->sublevels_up;
|
|
/* ignore local vars of subqueries */
|
|
if (varlevelsup >= 0)
|
|
{
|
|
if (context->min_varlevel < 0 ||
|
|
context->min_varlevel > varlevelsup)
|
|
{
|
|
context->min_varlevel = varlevelsup;
|
|
|
|
/*
|
|
* As soon as we find a local variable, we can abort the
|
|
* tree traversal, since min_varlevel is then certainly 0.
|
|
*/
|
|
if (varlevelsup == 0)
|
|
return true;
|
|
}
|
|
}
|
|
}
|
|
|
|
/*
|
|
* An Aggref must be treated like a Var of its level. Normally we'd
|
|
* get the same result from looking at the Vars in the aggregate's
|
|
* argument, but this fails in the case of a Var-less aggregate call
|
|
* (COUNT(*)).
|
|
*/
|
|
if (IsA(node, Aggref))
|
|
{
|
|
int agglevelsup = ((Aggref *) node)->agglevelsup;
|
|
|
|
/* convert levelsup to frame of reference of original query */
|
|
agglevelsup -= context->sublevels_up;
|
|
/* ignore local aggs of subqueries */
|
|
if (agglevelsup >= 0)
|
|
{
|
|
if (context->min_varlevel < 0 ||
|
|
context->min_varlevel > agglevelsup)
|
|
{
|
|
context->min_varlevel = agglevelsup;
|
|
|
|
/*
|
|
* As soon as we find a local aggregate, we can abort the
|
|
* tree traversal, since min_varlevel is then certainly 0.
|
|
*/
|
|
if (agglevelsup == 0)
|
|
return true;
|
|
}
|
|
}
|
|
}
|
|
if (IsA(node, Query))
|
|
{
|
|
/* Recurse into subselects */
|
|
bool result;
|
|
|
|
context->sublevels_up++;
|
|
result = query_tree_walker((Query *) node,
|
|
find_minimum_var_level_walker,
|
|
(void *) context,
|
|
0);
|
|
context->sublevels_up--;
|
|
return result;
|
|
}
|
|
return expression_tree_walker(node,
|
|
find_minimum_var_level_walker,
|
|
(void *) context);
|
|
}
|
|
|
|
|
|
/*
|
|
* pull_var_clause
|
|
* Recursively pulls all var nodes from an expression clause.
|
|
*
|
|
* Upper-level vars (with varlevelsup > 0) are included only
|
|
* if includeUpperVars is true. Most callers probably want
|
|
* to ignore upper-level vars.
|
|
*
|
|
* Returns list of varnodes found. Note the varnodes themselves are not
|
|
* copied, only referenced.
|
|
*
|
|
* Does not examine subqueries, therefore must only be used after reduction
|
|
* of sublinks to subplans!
|
|
*/
|
|
List *
|
|
pull_var_clause(Node *node, bool includeUpperVars)
|
|
{
|
|
pull_var_clause_context context;
|
|
|
|
FastListInit(&context.varlist);
|
|
context.includeUpperVars = includeUpperVars;
|
|
|
|
pull_var_clause_walker(node, &context);
|
|
return FastListValue(&context.varlist);
|
|
}
|
|
|
|
static bool
|
|
pull_var_clause_walker(Node *node, pull_var_clause_context *context)
|
|
{
|
|
if (node == NULL)
|
|
return false;
|
|
if (IsA(node, Var))
|
|
{
|
|
if (((Var *) node)->varlevelsup == 0 || context->includeUpperVars)
|
|
FastAppend(&context->varlist, node);
|
|
return false;
|
|
}
|
|
return expression_tree_walker(node, pull_var_clause_walker,
|
|
(void *) context);
|
|
}
|
|
|
|
|
|
/*
|
|
* flatten_join_alias_vars
|
|
* Replace Vars that reference JOIN outputs with references to the original
|
|
* relation variables instead. This allows quals involving such vars to be
|
|
* pushed down. Whole-row Vars that reference JOIN relations are expanded
|
|
* into RowExpr constructs that name the individual output Vars. This
|
|
* is necessary since we will not scan the JOIN as a base relation, which
|
|
* is the only way that the executor can directly handle whole-row Vars.
|
|
*
|
|
* NOTE: this is used on not-yet-planned expressions. We do not expect it
|
|
* to be applied directly to a Query node.
|
|
*/
|
|
Node *
|
|
flatten_join_alias_vars(Query *root, Node *node)
|
|
{
|
|
flatten_join_alias_vars_context context;
|
|
|
|
context.root = root;
|
|
context.sublevels_up = 0;
|
|
|
|
return flatten_join_alias_vars_mutator(node, &context);
|
|
}
|
|
|
|
static Node *
|
|
flatten_join_alias_vars_mutator(Node *node,
|
|
flatten_join_alias_vars_context *context)
|
|
{
|
|
if (node == NULL)
|
|
return NULL;
|
|
if (IsA(node, Var))
|
|
{
|
|
Var *var = (Var *) node;
|
|
RangeTblEntry *rte;
|
|
Node *newvar;
|
|
|
|
/* No change unless Var belongs to a JOIN of the target level */
|
|
if (var->varlevelsup != context->sublevels_up)
|
|
return node; /* no need to copy, really */
|
|
rte = rt_fetch(var->varno, context->root->rtable);
|
|
if (rte->rtekind != RTE_JOIN)
|
|
return node;
|
|
if (var->varattno == InvalidAttrNumber)
|
|
{
|
|
/* Must expand whole-row reference */
|
|
RowExpr *rowexpr;
|
|
List *fields = NIL;
|
|
ListCell *l;
|
|
|
|
foreach(l, rte->joinaliasvars)
|
|
{
|
|
newvar = (Node *) lfirst(l);
|
|
/*
|
|
* If we are expanding an alias carried down from an upper
|
|
* query, must adjust its varlevelsup fields.
|
|
*/
|
|
if (context->sublevels_up != 0)
|
|
{
|
|
newvar = copyObject(newvar);
|
|
IncrementVarSublevelsUp(newvar, context->sublevels_up, 0);
|
|
}
|
|
/* Recurse in case join input is itself a join */
|
|
newvar = flatten_join_alias_vars_mutator(newvar, context);
|
|
fields = lappend(fields, newvar);
|
|
}
|
|
rowexpr = makeNode(RowExpr);
|
|
rowexpr->args = fields;
|
|
rowexpr->row_typeid = var->vartype;
|
|
rowexpr->row_format = COERCE_IMPLICIT_CAST;
|
|
|
|
return (Node *) rowexpr;
|
|
}
|
|
|
|
/* Expand join alias reference */
|
|
Assert(var->varattno > 0);
|
|
newvar = (Node *) nth(var->varattno - 1, rte->joinaliasvars);
|
|
|
|
/*
|
|
* If we are expanding an alias carried down from an upper query,
|
|
* must adjust its varlevelsup fields.
|
|
*/
|
|
if (context->sublevels_up != 0)
|
|
{
|
|
newvar = copyObject(newvar);
|
|
IncrementVarSublevelsUp(newvar, context->sublevels_up, 0);
|
|
}
|
|
/* Recurse in case join input is itself a join */
|
|
return flatten_join_alias_vars_mutator(newvar, context);
|
|
}
|
|
if (IsA(node, InClauseInfo))
|
|
{
|
|
/* Copy the InClauseInfo node with correct mutation of subnodes */
|
|
InClauseInfo *ininfo;
|
|
|
|
ininfo = (InClauseInfo *) expression_tree_mutator(node,
|
|
flatten_join_alias_vars_mutator,
|
|
(void *) context);
|
|
/* now fix InClauseInfo's relid sets */
|
|
if (context->sublevels_up == 0)
|
|
{
|
|
ininfo->lefthand = alias_relid_set(context->root,
|
|
ininfo->lefthand);
|
|
ininfo->righthand = alias_relid_set(context->root,
|
|
ininfo->righthand);
|
|
}
|
|
return (Node *) ininfo;
|
|
}
|
|
|
|
if (IsA(node, Query))
|
|
{
|
|
/* Recurse into RTE subquery or not-yet-planned sublink subquery */
|
|
Query *newnode;
|
|
|
|
context->sublevels_up++;
|
|
newnode = query_tree_mutator((Query *) node,
|
|
flatten_join_alias_vars_mutator,
|
|
(void *) context,
|
|
QTW_IGNORE_JOINALIASES);
|
|
context->sublevels_up--;
|
|
return (Node *) newnode;
|
|
}
|
|
/* Already-planned tree not supported */
|
|
Assert(!is_subplan(node));
|
|
|
|
return expression_tree_mutator(node, flatten_join_alias_vars_mutator,
|
|
(void *) context);
|
|
}
|
|
|
|
/*
|
|
* alias_relid_set: in a set of RT indexes, replace joins by their
|
|
* underlying base relids
|
|
*/
|
|
static Relids
|
|
alias_relid_set(Query *root, Relids relids)
|
|
{
|
|
Relids result = NULL;
|
|
Relids tmprelids;
|
|
int rtindex;
|
|
|
|
tmprelids = bms_copy(relids);
|
|
while ((rtindex = bms_first_member(tmprelids)) >= 0)
|
|
{
|
|
RangeTblEntry *rte = rt_fetch(rtindex, root->rtable);
|
|
|
|
if (rte->rtekind == RTE_JOIN)
|
|
result = bms_join(result, get_relids_for_join(root, rtindex));
|
|
else
|
|
result = bms_add_member(result, rtindex);
|
|
}
|
|
bms_free(tmprelids);
|
|
return result;
|
|
}
|