mirror of
https://git.postgresql.org/git/postgresql.git
synced 2026-02-09 09:17:33 +08:00
If a plan node output expression returns an "expanded" datum, and that output column is referenced in more than one place in upper-level plan nodes, we need to ensure that what is returned is a read-only reference not a read/write reference. Otherwise one of the referencing sites could scribble on or even delete the expanded datum before we have evaluated the others. Commit 1dc5ebc9077ab742, which introduced this feature, supposed that it'd be sufficient to make SubqueryScan nodes force their output columns to read-only state. The folly of that was revealed by bug #14174 from Andrew Gierth, and really should have been immediately obvious considering that the planner will happily optimize SubqueryScan nodes out of the plan without any regard for this issue. The safest fix seems to be to make ExecProject() force its results into read-only state; that will cover every case where a plan node returns expression results. Actually we can delegate this to ExecTargetList() since we can recursively assume that plain Vars will not reference read-write datums. That should keep the extra overhead down to something minimal. We no longer need ExecMakeSlotContentsReadOnly(), which was introduced only in support of the idea that just a few plan node types would need to do this. In the future it would be nice to have the planner account for this problem and inject force-to-read-only expression evaluation nodes into only the places where there's a risk of multiple evaluation. That's not a suitable solution for 9.5 or even 9.6 at this point, though. Report: <20160603124628.9932.41279@wrigleys.postgresql.org>
210 lines
5.5 KiB
C
210 lines
5.5 KiB
C
/*-------------------------------------------------------------------------
|
|
*
|
|
* nodeSubqueryscan.c
|
|
* Support routines for scanning subqueries (subselects in rangetable).
|
|
*
|
|
* This is just enough different from sublinks (nodeSubplan.c) to mean that
|
|
* we need two sets of code. Ought to look at trying to unify the cases.
|
|
*
|
|
*
|
|
* Portions Copyright (c) 1996-2016, PostgreSQL Global Development Group
|
|
* Portions Copyright (c) 1994, Regents of the University of California
|
|
*
|
|
*
|
|
* IDENTIFICATION
|
|
* src/backend/executor/nodeSubqueryscan.c
|
|
*
|
|
*-------------------------------------------------------------------------
|
|
*/
|
|
/*
|
|
* INTERFACE ROUTINES
|
|
* ExecSubqueryScan scans a subquery.
|
|
* ExecSubqueryNext retrieve next tuple in sequential order.
|
|
* ExecInitSubqueryScan creates and initializes a subqueryscan node.
|
|
* ExecEndSubqueryScan releases any storage allocated.
|
|
* ExecReScanSubqueryScan rescans the relation
|
|
*
|
|
*/
|
|
#include "postgres.h"
|
|
|
|
#include "executor/execdebug.h"
|
|
#include "executor/nodeSubqueryscan.h"
|
|
|
|
static TupleTableSlot *SubqueryNext(SubqueryScanState *node);
|
|
|
|
/* ----------------------------------------------------------------
|
|
* Scan Support
|
|
* ----------------------------------------------------------------
|
|
*/
|
|
/* ----------------------------------------------------------------
|
|
* SubqueryNext
|
|
*
|
|
* This is a workhorse for ExecSubqueryScan
|
|
* ----------------------------------------------------------------
|
|
*/
|
|
static TupleTableSlot *
|
|
SubqueryNext(SubqueryScanState *node)
|
|
{
|
|
TupleTableSlot *slot;
|
|
|
|
/*
|
|
* Get the next tuple from the sub-query.
|
|
*/
|
|
slot = ExecProcNode(node->subplan);
|
|
|
|
/*
|
|
* We just return the subplan's result slot, rather than expending extra
|
|
* cycles for ExecCopySlot(). (Our own ScanTupleSlot is used only for
|
|
* EvalPlanQual rechecks.)
|
|
*/
|
|
return slot;
|
|
}
|
|
|
|
/*
|
|
* SubqueryRecheck -- access method routine to recheck a tuple in EvalPlanQual
|
|
*/
|
|
static bool
|
|
SubqueryRecheck(SubqueryScanState *node, TupleTableSlot *slot)
|
|
{
|
|
/* nothing to check */
|
|
return true;
|
|
}
|
|
|
|
/* ----------------------------------------------------------------
|
|
* ExecSubqueryScan(node)
|
|
*
|
|
* Scans the subquery sequentially and returns the next qualifying
|
|
* tuple.
|
|
* We call the ExecScan() routine and pass it the appropriate
|
|
* access method functions.
|
|
* ----------------------------------------------------------------
|
|
*/
|
|
TupleTableSlot *
|
|
ExecSubqueryScan(SubqueryScanState *node)
|
|
{
|
|
return ExecScan(&node->ss,
|
|
(ExecScanAccessMtd) SubqueryNext,
|
|
(ExecScanRecheckMtd) SubqueryRecheck);
|
|
}
|
|
|
|
/* ----------------------------------------------------------------
|
|
* ExecInitSubqueryScan
|
|
* ----------------------------------------------------------------
|
|
*/
|
|
SubqueryScanState *
|
|
ExecInitSubqueryScan(SubqueryScan *node, EState *estate, int eflags)
|
|
{
|
|
SubqueryScanState *subquerystate;
|
|
|
|
/* check for unsupported flags */
|
|
Assert(!(eflags & EXEC_FLAG_MARK));
|
|
|
|
/* SubqueryScan should not have any "normal" children */
|
|
Assert(outerPlan(node) == NULL);
|
|
Assert(innerPlan(node) == NULL);
|
|
|
|
/*
|
|
* create state structure
|
|
*/
|
|
subquerystate = makeNode(SubqueryScanState);
|
|
subquerystate->ss.ps.plan = (Plan *) node;
|
|
subquerystate->ss.ps.state = estate;
|
|
|
|
/*
|
|
* Miscellaneous initialization
|
|
*
|
|
* create expression context for node
|
|
*/
|
|
ExecAssignExprContext(estate, &subquerystate->ss.ps);
|
|
|
|
/*
|
|
* initialize child expressions
|
|
*/
|
|
subquerystate->ss.ps.targetlist = (List *)
|
|
ExecInitExpr((Expr *) node->scan.plan.targetlist,
|
|
(PlanState *) subquerystate);
|
|
subquerystate->ss.ps.qual = (List *)
|
|
ExecInitExpr((Expr *) node->scan.plan.qual,
|
|
(PlanState *) subquerystate);
|
|
|
|
/*
|
|
* tuple table initialization
|
|
*/
|
|
ExecInitResultTupleSlot(estate, &subquerystate->ss.ps);
|
|
ExecInitScanTupleSlot(estate, &subquerystate->ss);
|
|
|
|
/*
|
|
* initialize subquery
|
|
*/
|
|
subquerystate->subplan = ExecInitNode(node->subplan, estate, eflags);
|
|
|
|
subquerystate->ss.ps.ps_TupFromTlist = false;
|
|
|
|
/*
|
|
* Initialize scan tuple type (needed by ExecAssignScanProjectionInfo)
|
|
*/
|
|
ExecAssignScanType(&subquerystate->ss,
|
|
ExecGetResultType(subquerystate->subplan));
|
|
|
|
/*
|
|
* Initialize result tuple type and projection info.
|
|
*/
|
|
ExecAssignResultTypeFromTL(&subquerystate->ss.ps);
|
|
ExecAssignScanProjectionInfo(&subquerystate->ss);
|
|
|
|
return subquerystate;
|
|
}
|
|
|
|
/* ----------------------------------------------------------------
|
|
* ExecEndSubqueryScan
|
|
*
|
|
* frees any storage allocated through C routines.
|
|
* ----------------------------------------------------------------
|
|
*/
|
|
void
|
|
ExecEndSubqueryScan(SubqueryScanState *node)
|
|
{
|
|
/*
|
|
* Free the exprcontext
|
|
*/
|
|
ExecFreeExprContext(&node->ss.ps);
|
|
|
|
/*
|
|
* clean out the upper tuple table
|
|
*/
|
|
ExecClearTuple(node->ss.ps.ps_ResultTupleSlot);
|
|
ExecClearTuple(node->ss.ss_ScanTupleSlot);
|
|
|
|
/*
|
|
* close down subquery
|
|
*/
|
|
ExecEndNode(node->subplan);
|
|
}
|
|
|
|
/* ----------------------------------------------------------------
|
|
* ExecReScanSubqueryScan
|
|
*
|
|
* Rescans the relation.
|
|
* ----------------------------------------------------------------
|
|
*/
|
|
void
|
|
ExecReScanSubqueryScan(SubqueryScanState *node)
|
|
{
|
|
ExecScanReScan(&node->ss);
|
|
|
|
/*
|
|
* ExecReScan doesn't know about my subplan, so I have to do
|
|
* changed-parameter signaling myself. This is just as well, because the
|
|
* subplan has its own memory context in which its chgParam state lives.
|
|
*/
|
|
if (node->ss.ps.chgParam != NULL)
|
|
UpdateChangedParamSet(node->subplan, node->ss.ps.chgParam);
|
|
|
|
/*
|
|
* if chgParam of subnode is not null then plan will be re-scanned by
|
|
* first ExecProcNode.
|
|
*/
|
|
if (node->subplan->chgParam == NULL)
|
|
ExecReScan(node->subplan);
|
|
}
|