postgresql/src/backend/executor/nodeSubplan.c

296 lines
7.2 KiB
C

/*-------------------------------------------------------------------------
*
* nodeSubplan.c--
* routines to support subselects
*
*-------------------------------------------------------------------------
*/
/*
* INTERFACE ROUTINES
* ExecSubPlan - process a subselect
* ExecInitSubPlan - initialize a subselect
* ExecEndSubPlan - shut down a subselect
*/
#include "postgres.h"
#include "access/heapam.h"
#include "tcop/pquery.h"
#include "executor/executor.h"
#include "executor/execdebug.h"
#include "executor/nodeSubplan.h"
/* ----------------------------------------------------------------
* ExecSubPlan(node)
*
* ----------------------------------------------------------------
*/
Datum
ExecSubPlan(SubPlan *node, List *pvar, ExprContext *econtext)
{
Plan *plan = node->plan;
SubLink *sublink = node->sublink;
TupleTableSlot *slot;
List *lst;
bool result = false;
bool found = false;
if (node->setParam != NULL)
elog(ERROR, "ExecSubPlan: can't set parent params from subquery");
/*
* Set Params of this plan from parent plan correlation Vars
*/
if (node->parParam != NULL)
{
foreach(lst, node->parParam)
{
ParamExecData *prm = &(econtext->ecxt_param_exec_vals[lfirsti(lst)]);
prm->value = ExecEvalExpr((Node *) lfirst(pvar),
econtext,
&(prm->isnull), NULL);
pvar = lnext(pvar);
}
plan->chgParam = nconc(plan->chgParam, listCopy(node->parParam));
}
ExecReScan(plan, (ExprContext *) NULL, plan);
for (slot = ExecProcNode(plan, plan);
!TupIsNull(slot);
slot = ExecProcNode(plan, plan))
{
HeapTuple tup = slot->val;
TupleDesc tdesc = slot->ttc_tupleDescriptor;
int i = 1;
if (sublink->subLinkType == EXPR_SUBLINK && found)
{
elog(ERROR, "ExecSubPlan: more than one tuple returned by expression subselect");
return (Datum) false;
}
if (sublink->subLinkType == EXISTS_SUBLINK)
return (Datum) true;
found = true;
foreach(lst, sublink->oper)
{
Expr *expr = (Expr *) lfirst(lst);
Const *con = lsecond(expr->args);
bool isnull;
con->constvalue = heap_getattr(tup, i, tdesc, &(con->constisnull));
result = (bool) ExecEvalExpr((Node *) expr, econtext, &isnull, (bool *) NULL);
if (isnull)
result = false;
if ((!result && !(sublink->useor)) || (result && sublink->useor))
break;
i++;
}
if ((!result && sublink->subLinkType == ALL_SUBLINK) ||
(result && sublink->subLinkType == ANY_SUBLINK))
break;
}
if (!found && sublink->subLinkType == ALL_SUBLINK)
return (Datum) true;
return (Datum) result;
}
/* ----------------------------------------------------------------
* ExecInitSubPlan
*
* ----------------------------------------------------------------
*/
extern void ExecCheckPerms(CmdType op, int resRel, List *rtable, Query *q);
bool
ExecInitSubPlan(SubPlan *node, EState *estate, Plan *parent)
{
EState *sp_estate = CreateExecutorState();
ExecCheckPerms(CMD_SELECT, 0, node->rtable, (Query *) NULL);
sp_estate->es_range_table = node->rtable;
sp_estate->es_param_list_info = estate->es_param_list_info;
sp_estate->es_param_exec_vals = estate->es_param_exec_vals;
sp_estate->es_tupleTable =
ExecCreateTupleTable(ExecCountSlotsNode(node->plan) + 10);
pfree(sp_estate->es_refcount);
sp_estate->es_refcount = estate->es_refcount;
if (!ExecInitNode(node->plan, sp_estate, NULL))
return false;
node->shutdown = true;
/*
* If this plan is un-correlated or undirect correlated one and want
* to set params for parent plan then prepare parameters.
*/
if (node->setParam != NULL)
{
List *lst;
foreach(lst, node->setParam)
{
ParamExecData *prm = &(estate->es_param_exec_vals[lfirsti(lst)]);
prm->execPlan = node;
}
/*
* Note that in the case of un-correlated subqueries we don't care
* about setting parent->chgParam here: indices take care about
* it, for others - it doesn't matter...
*/
}
return true;
}
/* ----------------------------------------------------------------
* ExecSetParamPlan
*
* Executes plan of node and sets parameters.
* ----------------------------------------------------------------
*/
void
ExecSetParamPlan(SubPlan *node)
{
Plan *plan = node->plan;
SubLink *sublink = node->sublink;
TupleTableSlot *slot;
List *lst;
bool found = false;
if (sublink->subLinkType == ANY_SUBLINK ||
sublink->subLinkType == ALL_SUBLINK)
elog(ERROR, "ExecSetParamPlan: ANY/ALL subselect unsupported");
if (plan->chgParam != NULL)
ExecReScan(plan, (ExprContext *) NULL, plan);
for (slot = ExecProcNode(plan, plan);
!TupIsNull(slot);
slot = ExecProcNode(plan, plan))
{
HeapTuple tup = slot->val;
TupleDesc tdesc = slot->ttc_tupleDescriptor;
int i = 1;
if (sublink->subLinkType == EXPR_SUBLINK && found)
{
elog(ERROR, "ExecSetParamPlan: more than one tuple returned by expression subselect");
return;
}
found = true;
if (sublink->subLinkType == EXISTS_SUBLINK)
{
ParamExecData *prm = &(plan->state->es_param_exec_vals[lfirsti(node->setParam)]);
prm->execPlan = NULL;
prm->value = (Datum) true;
prm->isnull = false;
break;
}
/*
* If this is uncorrelated subquery then its plan will be closed
* (see below) and this tuple will be free-ed - bad for not byval
* types... But is free-ing possible in the next ExecProcNode in
* this loop ? Who knows... Someday we'll keep track of saved
* tuples...
*/
tup = heap_copytuple(tup);
foreach(lst, node->setParam)
{
ParamExecData *prm = &(plan->state->es_param_exec_vals[lfirsti(lst)]);
prm->execPlan = NULL;
prm->value = heap_getattr(tup, i, tdesc, &(prm->isnull));
i++;
}
}
if (!found)
{
if (sublink->subLinkType == EXISTS_SUBLINK)
{
ParamExecData *prm = &(plan->state->es_param_exec_vals[lfirsti(node->setParam)]);
prm->execPlan = NULL;
prm->value = (Datum) false;
prm->isnull = false;
}
else
{
foreach(lst, node->setParam)
{
ParamExecData *prm = &(plan->state->es_param_exec_vals[lfirsti(lst)]);
prm->execPlan = NULL;
prm->value = (Datum) NULL;
prm->isnull = true;
}
}
}
if (plan->extParam == NULL) /* un-correlated ... */
{
ExecEndNode(plan, plan);
node->shutdown = false;
}
}
/* ----------------------------------------------------------------
* ExecEndSubPlan
* ----------------------------------------------------------------
*/
void
ExecEndSubPlan(SubPlan *node)
{
if (node->shutdown)
{
ExecEndNode(node->plan, node->plan);
node->shutdown = false;
}
}
void
ExecReScanSetParamPlan(SubPlan *node, Plan *parent)
{
Plan *plan = node->plan;
List *lst;
if (node->parParam != NULL)
elog(ERROR, "ExecReScanSetParamPlan: direct correlated subquery unsupported, yet");
if (node->setParam == NULL)
elog(ERROR, "ExecReScanSetParamPlan: setParam list is NULL");
if (plan->extParam == NULL)
elog(ERROR, "ExecReScanSetParamPlan: extParam list of plan is NULL");
/*
* Don't actual re-scan: ExecSetParamPlan does re-scan if
* node->plan->chgParam is not NULL... ExecReScan (plan, NULL, plan);
*/
foreach(lst, node->setParam)
{
ParamExecData *prm = &(plan->state->es_param_exec_vals[lfirsti(lst)]);
prm->execPlan = node;
}
parent->chgParam = nconc(parent->chgParam, listCopy(node->setParam));
}