mirror of
https://github.com/postgres/postgres.git
synced 2025-05-20 05:13:53 +03:00
now. Here some tested features, (examples included in the patch): 1.1) Subselects in the having clause 1.2) Double nested subselects 1.3) Subselects used in the where clause and in the having clause simultaneously 1.4) Union Selects using having 1.5) Indexes on the base relations are used correctly 1.6) Unallowed Queries are prevented (e.g. qualifications in the having clause that belong to the where clause) 1.7) Insert into as select 2) Queries using the having clause on view relations also work but there are some restrictions: 2.1) Create View as Select ... Having ...; using base tables in the select 2.1.1) The Query rewrite system: 2.1.2) Why are only simple queries allowed against a view from 2.1) ? 2.2) Select ... from testview1, testview2, ... having...; 3) Bug in ExecMergeJoin ?? Regards Stefan
558 lines
14 KiB
C
558 lines
14 KiB
C
/*-------------------------------------------------------------------------
|
|
*
|
|
* subselect.c--
|
|
*
|
|
*-------------------------------------------------------------------------
|
|
*/
|
|
#include "postgres.h"
|
|
|
|
#include "catalog/pg_type.h"
|
|
|
|
#include "nodes/pg_list.h"
|
|
#include "nodes/plannodes.h"
|
|
#include "nodes/parsenodes.h"
|
|
#include "nodes/relation.h"
|
|
#include "nodes/makefuncs.h"
|
|
#include "nodes/nodeFuncs.h"
|
|
|
|
#include "optimizer/subselect.h"
|
|
#include "optimizer/planner.h"
|
|
#include "optimizer/planmain.h"
|
|
#include "optimizer/internal.h"
|
|
#include "optimizer/paths.h"
|
|
#include "optimizer/clauses.h"
|
|
#include "optimizer/keys.h"
|
|
#include "optimizer/tlist.h"
|
|
#include "optimizer/var.h"
|
|
#include "optimizer/cost.h"
|
|
|
|
int PlannerQueryLevel; /* level of current query */
|
|
List *PlannerVarParam; /* correlation Vars to Param mapper */
|
|
List *PlannerParamVar; /* to get Var from Param->paramid */
|
|
List *PlannerInitPlan; /* init subplans for current query */
|
|
int PlannerPlanId;
|
|
|
|
|
|
static int
|
|
_new_param(Var *var, int varlevel)
|
|
{
|
|
List *last;
|
|
int i = 0;
|
|
|
|
if (PlannerParamVar == NULL)
|
|
last = PlannerParamVar = makeNode(List);
|
|
else
|
|
{
|
|
for (last = PlannerParamVar;;)
|
|
{
|
|
i++;
|
|
if (lnext(last) == NULL)
|
|
break;
|
|
last = lnext(last);
|
|
}
|
|
lnext(last) = makeNode(List);
|
|
last = lnext(last);
|
|
}
|
|
|
|
lnext(last) = NULL;
|
|
lfirst(last) = makeVar(var->varno, var->varattno, var->vartype,
|
|
var->vartypmod, varlevel, var->varnoold, var->varoattno);
|
|
|
|
return (i);
|
|
}
|
|
|
|
static Param *
|
|
_replace_var(Var *var)
|
|
{
|
|
List **rt = (List **) nth(var->varlevelsup, PlannerVarParam);
|
|
List *vpe = rt[var->varno - 1];
|
|
Param *retval;
|
|
int i;
|
|
|
|
if (vpe == NULL)
|
|
{
|
|
vpe = rt[var->varno - 1] = makeNode(List);
|
|
lfirsti(vpe) = -1;
|
|
lnext(vpe) = NULL;
|
|
}
|
|
|
|
for (i = 1;; i++)
|
|
{
|
|
if (i == var->varattno)
|
|
break;
|
|
if (lnext(vpe) == NULL)
|
|
{
|
|
lnext(vpe) = makeNode(List);
|
|
vpe = lnext(vpe);
|
|
lfirsti(vpe) = -1;
|
|
lnext(vpe) = NULL;
|
|
}
|
|
else
|
|
vpe = lnext(vpe);
|
|
}
|
|
|
|
if ((i = lfirsti(vpe)) < 0) /* parameter is not assigned */
|
|
i = _new_param(var, PlannerQueryLevel - var->varlevelsup);
|
|
|
|
retval = makeNode(Param);
|
|
retval->paramkind = PARAM_EXEC;
|
|
retval->paramid = (AttrNumber) i;
|
|
retval->paramtype = var->vartype;
|
|
|
|
return (retval);
|
|
}
|
|
|
|
static Node *
|
|
_make_subplan(SubLink *slink)
|
|
{
|
|
SubPlan *node = makeNode(SubPlan);
|
|
Plan *plan;
|
|
List *lst;
|
|
Node *result;
|
|
List *saved_ip = PlannerInitPlan;
|
|
|
|
PlannerInitPlan = NULL;
|
|
|
|
PlannerQueryLevel++; /* we becomes child */
|
|
|
|
node->plan = plan = union_planner((Query *) slink->subselect);
|
|
|
|
/*
|
|
* Assign subPlan, extParam and locParam to plan nodes. At the moment,
|
|
* SS_finalize_plan doesn't handle initPlan-s and so we assigne them
|
|
* to the topmost plan node and take care about its extParam too.
|
|
*/
|
|
(void) SS_finalize_plan(plan);
|
|
plan->initPlan = PlannerInitPlan;
|
|
|
|
/* Get extParam from InitPlan-s */
|
|
foreach(lst, PlannerInitPlan)
|
|
{
|
|
List *lp;
|
|
|
|
foreach(lp, ((SubPlan *) lfirst(lst))->plan->extParam)
|
|
{
|
|
if (!intMember(lfirsti(lp), plan->extParam))
|
|
plan->extParam = lappendi(plan->extParam, lfirsti(lp));
|
|
}
|
|
}
|
|
|
|
/* and now we are parent again */
|
|
PlannerInitPlan = saved_ip;
|
|
PlannerQueryLevel--;
|
|
|
|
node->plan_id = PlannerPlanId++;
|
|
node->rtable = ((Query *) slink->subselect)->rtable;
|
|
node->sublink = slink;
|
|
slink->subselect = NULL; /* cool ?! */
|
|
|
|
/* make parParam list */
|
|
foreach(lst, plan->extParam)
|
|
{
|
|
Var *var = nth(lfirsti(lst), PlannerParamVar);
|
|
|
|
if (var->varlevelsup == PlannerQueryLevel)
|
|
node->parParam = lappendi(node->parParam, lfirsti(lst));
|
|
}
|
|
|
|
/*
|
|
* Un-correlated or undirect correlated plans of EXISTS or EXPR types
|
|
* can be used as initPlans...
|
|
*/
|
|
if (node->parParam == NULL && slink->subLinkType == EXPR_SUBLINK)
|
|
{
|
|
int i = 0;
|
|
|
|
/* transform right side of all sublink Oper-s into Param */
|
|
foreach(lst, slink->oper)
|
|
{
|
|
List *rside = lnext(((Expr *) lfirst(lst))->args);
|
|
TargetEntry *te = nth(i, plan->targetlist);
|
|
Var *var = makeVar(0, 0, te->resdom->restype,
|
|
te->resdom->restypmod,
|
|
PlannerQueryLevel, 0, 0);
|
|
Param *prm = makeNode(Param);
|
|
|
|
prm->paramkind = PARAM_EXEC;
|
|
prm->paramid = (AttrNumber) _new_param(var, PlannerQueryLevel);
|
|
prm->paramtype = var->vartype;
|
|
lfirst(rside) = prm;
|
|
node->setParam = lappendi(node->setParam, prm->paramid);
|
|
pfree(var);
|
|
i++;
|
|
}
|
|
PlannerInitPlan = lappend(PlannerInitPlan, node);
|
|
if (i > 1)
|
|
result = (Node *) ((slink->useor) ? make_orclause(slink->oper) :
|
|
make_andclause(slink->oper));
|
|
else
|
|
result = (Node *) lfirst(slink->oper);
|
|
}
|
|
else if (node->parParam == NULL && slink->subLinkType == EXISTS_SUBLINK)
|
|
{
|
|
Var *var = makeVar(0, 0, BOOLOID, -1, PlannerQueryLevel, 0, 0);
|
|
Param *prm = makeNode(Param);
|
|
|
|
prm->paramkind = PARAM_EXEC;
|
|
prm->paramid = (AttrNumber) _new_param(var, PlannerQueryLevel);
|
|
prm->paramtype = var->vartype;
|
|
node->setParam = lappendi(node->setParam, prm->paramid);
|
|
pfree(var);
|
|
PlannerInitPlan = lappend(PlannerInitPlan, node);
|
|
result = (Node *) prm;
|
|
}
|
|
else
|
|
/* make expression of SUBPLAN type */
|
|
{
|
|
Expr *expr = makeNode(Expr);
|
|
List *args = NULL;
|
|
int i = 0;
|
|
|
|
expr->typeOid = BOOLOID;
|
|
expr->opType = SUBPLAN_EXPR;
|
|
expr->oper = (Node *) node;
|
|
|
|
/*
|
|
* Make expr->args from parParam. Left sides of sublink Oper-s are
|
|
* handled by optimizer directly... Also, transform right side of
|
|
* sublink Oper-s into Const.
|
|
*/
|
|
foreach(lst, node->parParam)
|
|
{
|
|
Var *var = nth(lfirsti(lst), PlannerParamVar);
|
|
|
|
var = (Var *) copyObject(var);
|
|
var->varlevelsup = 0;
|
|
args = lappend(args, var);
|
|
}
|
|
foreach(lst, slink->oper)
|
|
{
|
|
List *rside = lnext(((Expr *) lfirst(lst))->args);
|
|
TargetEntry *te = nth(i, plan->targetlist);
|
|
Const *con = makeConst(te->resdom->restype,
|
|
0, 0, true, 0, 0, 0);
|
|
|
|
lfirst(rside) = con;
|
|
i++;
|
|
}
|
|
expr->args = args;
|
|
result = (Node *) expr;
|
|
}
|
|
|
|
return (result);
|
|
|
|
}
|
|
|
|
static List *
|
|
set_unioni(List *l1, List *l2)
|
|
{
|
|
if (l1 == NULL)
|
|
return (l2);
|
|
if (l2 == NULL)
|
|
return (l1);
|
|
|
|
return (nconc(l1, set_differencei(l2, l1)));
|
|
}
|
|
|
|
static List *
|
|
_finalize_primnode(void *expr, List **subplan)
|
|
{
|
|
List *result = NULL;
|
|
|
|
if (expr == NULL)
|
|
return (NULL);
|
|
|
|
if (IsA(expr, Param))
|
|
{
|
|
if (((Param *) expr)->paramkind == PARAM_EXEC)
|
|
return (lconsi(((Param *) expr)->paramid, (List *) NULL));
|
|
}
|
|
else if (single_node(expr))
|
|
return (NULL);
|
|
else if (IsA(expr, List))
|
|
{
|
|
List *le;
|
|
|
|
foreach(le, (List *) expr)
|
|
result = set_unioni(result,
|
|
_finalize_primnode(lfirst(le), subplan));
|
|
}
|
|
else if (IsA(expr, Iter))
|
|
return (_finalize_primnode(((Iter *) expr)->iterexpr, subplan));
|
|
else if (or_clause(expr) || and_clause(expr) || is_opclause(expr) ||
|
|
not_clause(expr) || is_funcclause(expr))
|
|
return (_finalize_primnode(((Expr *) expr)->args, subplan));
|
|
else if (IsA(expr, Aggreg))
|
|
return (_finalize_primnode(((Aggreg *) expr)->target, subplan));
|
|
else if (IsA(expr, ArrayRef))
|
|
{
|
|
result = _finalize_primnode(((ArrayRef *) expr)->refupperindexpr, subplan);
|
|
result = set_unioni(result,
|
|
_finalize_primnode(((ArrayRef *) expr)->reflowerindexpr, subplan));
|
|
result = set_unioni(result,
|
|
_finalize_primnode(((ArrayRef *) expr)->refexpr, subplan));
|
|
result = set_unioni(result,
|
|
_finalize_primnode(((ArrayRef *) expr)->refassgnexpr, subplan));
|
|
}
|
|
else if (IsA(expr, TargetEntry))
|
|
return (_finalize_primnode(((TargetEntry *) expr)->expr, subplan));
|
|
else if (is_subplan(expr))
|
|
{
|
|
List *lst;
|
|
|
|
*subplan = lappend(*subplan, ((Expr *) expr)->oper);
|
|
foreach(lst, ((SubPlan *) ((Expr *) expr)->oper)->plan->extParam)
|
|
{
|
|
Var *var = nth(lfirsti(lst), PlannerParamVar);
|
|
|
|
if (var->varlevelsup < PlannerQueryLevel &&
|
|
!intMember(lfirsti(lst), result))
|
|
result = lappendi(result, lfirsti(lst));
|
|
}
|
|
}
|
|
else
|
|
elog(ERROR, "_finalize_primnode: can't handle node %d",
|
|
nodeTag(expr));
|
|
|
|
return (result);
|
|
}
|
|
|
|
Node *
|
|
SS_replace_correlation_vars(Node *expr)
|
|
{
|
|
|
|
if (expr == NULL)
|
|
return (NULL);
|
|
if (IsA(expr, List))
|
|
{
|
|
List *le;
|
|
|
|
foreach(le, (List *) expr)
|
|
lfirst(le) = SS_replace_correlation_vars((Node *) lfirst(le));
|
|
}
|
|
else if (IsA(expr, Var))
|
|
{
|
|
if (((Var *) expr)->varlevelsup > 0)
|
|
{
|
|
Assert(((Var *) expr)->varlevelsup < PlannerQueryLevel);
|
|
expr = (Node *) _replace_var((Var *) expr);
|
|
}
|
|
}
|
|
else if (IsA(expr, Iter))
|
|
{
|
|
((Iter *) expr)->iterexpr =
|
|
SS_replace_correlation_vars(((Iter *) expr)->iterexpr);
|
|
}
|
|
else if (single_node(expr))
|
|
return (expr);
|
|
else if (or_clause(expr) || and_clause(expr) || is_opclause(expr) ||
|
|
not_clause(expr) || is_funcclause(expr))
|
|
((Expr *) expr)->args = (List *)
|
|
SS_replace_correlation_vars((Node *) ((Expr *) expr)->args);
|
|
else if (IsA(expr, Aggreg))
|
|
((Aggreg *) expr)->target =
|
|
SS_replace_correlation_vars((Node *) ((Aggreg *) expr)->target);
|
|
else if (IsA(expr, ArrayRef))
|
|
{
|
|
((ArrayRef *) expr)->refupperindexpr = (List *)
|
|
SS_replace_correlation_vars((Node *) ((ArrayRef *) expr)->refupperindexpr);
|
|
((ArrayRef *) expr)->reflowerindexpr = (List *)
|
|
SS_replace_correlation_vars((Node *) ((ArrayRef *) expr)->reflowerindexpr);
|
|
((ArrayRef *) expr)->refexpr =
|
|
SS_replace_correlation_vars((Node *) ((ArrayRef *) expr)->refexpr);
|
|
((ArrayRef *) expr)->refassgnexpr =
|
|
SS_replace_correlation_vars(((ArrayRef *) expr)->refassgnexpr);
|
|
}
|
|
else if (IsA(expr, TargetEntry))
|
|
((TargetEntry *) expr)->expr =
|
|
SS_replace_correlation_vars((Node *) ((TargetEntry *) expr)->expr);
|
|
else if (IsA(expr, SubLink))
|
|
{
|
|
List *le;
|
|
|
|
foreach(le, ((SubLink *) expr)->oper) /* left sides only */
|
|
{
|
|
List *oparg = ((Expr *) lfirst(le))->args;
|
|
|
|
lfirst(oparg) = (List *)
|
|
SS_replace_correlation_vars((Node *) lfirst(oparg));
|
|
}
|
|
((SubLink *) expr)->lefthand = (List *)
|
|
SS_replace_correlation_vars((Node *) ((SubLink *) expr)->lefthand);
|
|
}
|
|
else
|
|
elog(NOTICE, "SS_replace_correlation_vars: can't handle node %d",
|
|
nodeTag(expr));
|
|
|
|
return (expr);
|
|
}
|
|
|
|
Node *
|
|
SS_process_sublinks(Node *expr)
|
|
{
|
|
if (expr == NULL)
|
|
return (NULL);
|
|
if (IsA(expr, List))
|
|
{
|
|
List *le;
|
|
|
|
foreach(le, (List *) expr)
|
|
lfirst(le) = SS_process_sublinks((Node *) lfirst(le));
|
|
}
|
|
else if (or_clause(expr) || and_clause(expr) || is_opclause(expr) ||
|
|
not_clause(expr) || is_funcclause(expr))
|
|
((Expr *) expr)->args = (List *)
|
|
SS_process_sublinks((Node *) ((Expr *) expr)->args);
|
|
else if (IsA(expr, SubLink))/* got it! */
|
|
{
|
|
lfirst(((Expr *) lfirst(((SubLink *)expr)->oper))->args) =
|
|
lfirst(((SubLink *)expr)->lefthand);
|
|
|
|
expr = _make_subplan((SubLink *) expr);
|
|
}
|
|
|
|
return (expr);
|
|
}
|
|
|
|
List *
|
|
SS_finalize_plan(Plan *plan)
|
|
{
|
|
List *extParam = NULL;
|
|
List *locParam = NULL;
|
|
List *subPlan = NULL;
|
|
List *param_list;
|
|
List *lst;
|
|
|
|
if (plan == NULL)
|
|
return (NULL);
|
|
|
|
param_list = _finalize_primnode(plan->targetlist, &subPlan);
|
|
Assert(subPlan == NULL);
|
|
|
|
switch (nodeTag(plan))
|
|
{
|
|
case T_Result:
|
|
param_list = set_unioni(param_list,
|
|
_finalize_primnode(((Result *) plan)->resconstantqual, &subPlan));
|
|
break;
|
|
|
|
case T_Append:
|
|
foreach(lst, ((Append *) plan)->appendplans)
|
|
param_list = set_unioni(param_list,
|
|
SS_finalize_plan((Plan *) lfirst(lst)));
|
|
break;
|
|
|
|
case T_IndexScan:
|
|
param_list = set_unioni(param_list,
|
|
_finalize_primnode(((IndexScan *) plan)->indxqual, &subPlan));
|
|
Assert(subPlan == NULL);
|
|
break;
|
|
|
|
case T_MergeJoin:
|
|
param_list = set_unioni(param_list,
|
|
_finalize_primnode(((MergeJoin *) plan)->mergeclauses, &subPlan));
|
|
Assert(subPlan == NULL);
|
|
break;
|
|
|
|
case T_HashJoin:
|
|
param_list = set_unioni(param_list,
|
|
_finalize_primnode(((HashJoin *) plan)->hashclauses, &subPlan));
|
|
Assert(subPlan == NULL);
|
|
break;
|
|
|
|
case T_Hash:
|
|
param_list = set_unioni(param_list,
|
|
_finalize_primnode(((Hash *) plan)->hashkey, &subPlan));
|
|
Assert(subPlan == NULL);
|
|
break;
|
|
|
|
case T_Agg:
|
|
param_list = set_unioni(param_list,
|
|
_finalize_primnode(((Agg *) plan)->aggs, &subPlan));
|
|
Assert(subPlan == NULL);
|
|
break;
|
|
|
|
case T_SeqScan:
|
|
case T_NestLoop:
|
|
case T_Material:
|
|
case T_Sort:
|
|
case T_Unique:
|
|
case T_Group:
|
|
break;
|
|
default:
|
|
elog(ERROR, "SS_finalize_plan: node %d unsupported", nodeTag(plan));
|
|
return (NULL);
|
|
}
|
|
|
|
param_list = set_unioni(param_list, _finalize_primnode(plan->qual, &subPlan));
|
|
param_list = set_unioni(param_list, SS_finalize_plan(plan->lefttree));
|
|
param_list = set_unioni(param_list, SS_finalize_plan(plan->righttree));
|
|
|
|
foreach(lst, param_list)
|
|
{
|
|
Var *var = nth(lfirsti(lst), PlannerParamVar);
|
|
|
|
if (var->varlevelsup < PlannerQueryLevel)
|
|
extParam = lappendi(extParam, lfirsti(lst));
|
|
else if (var->varlevelsup > PlannerQueryLevel)
|
|
elog(ERROR, "SS_finalize_plan: plan shouldn't reference subplan' variable");
|
|
else
|
|
{
|
|
Assert(var->varno == 0 && var->varattno == 0);
|
|
locParam = lappendi(locParam, lfirsti(lst));
|
|
}
|
|
}
|
|
|
|
plan->extParam = extParam;
|
|
plan->locParam = locParam;
|
|
plan->subPlan = subPlan;
|
|
|
|
return (param_list);
|
|
|
|
}
|
|
|
|
List *SS_pull_subplan(void *expr);
|
|
|
|
List *
|
|
SS_pull_subplan(void *expr)
|
|
{
|
|
List *result = NULL;
|
|
|
|
if (expr == NULL || single_node(expr))
|
|
return (NULL);
|
|
|
|
if (IsA(expr, List))
|
|
{
|
|
List *le;
|
|
|
|
foreach(le, (List *) expr)
|
|
result = nconc(result, SS_pull_subplan(lfirst(le)));
|
|
}
|
|
else if (IsA(expr, Iter))
|
|
return (SS_pull_subplan(((Iter *) expr)->iterexpr));
|
|
else if (or_clause(expr) || and_clause(expr) || is_opclause(expr) ||
|
|
not_clause(expr) || is_funcclause(expr))
|
|
return (SS_pull_subplan(((Expr *) expr)->args));
|
|
else if (IsA(expr, Aggreg))
|
|
return (SS_pull_subplan(((Aggreg *) expr)->target));
|
|
else if (IsA(expr, ArrayRef))
|
|
{
|
|
result = SS_pull_subplan(((ArrayRef *) expr)->refupperindexpr);
|
|
result = nconc(result,
|
|
SS_pull_subplan(((ArrayRef *) expr)->reflowerindexpr));
|
|
result = nconc(result,
|
|
SS_pull_subplan(((ArrayRef *) expr)->refexpr));
|
|
result = nconc(result,
|
|
SS_pull_subplan(((ArrayRef *) expr)->refassgnexpr));
|
|
}
|
|
else if (IsA(expr, TargetEntry))
|
|
return (SS_pull_subplan(((TargetEntry *) expr)->expr));
|
|
else if (is_subplan(expr))
|
|
return (lcons(((Expr *) expr)->oper, NULL));
|
|
else
|
|
elog(ERROR, "SS_pull_subplan: can't handle node %d",
|
|
nodeTag(expr));
|
|
|
|
return (result);
|
|
}
|