mirror of
https://github.com/postgres/postgres.git
synced 2025-05-15 19:15:29 +03:00
334 lines
9.0 KiB
C
334 lines
9.0 KiB
C
/*-------------------------------------------------------------------------
|
|
*
|
|
* nodeValuesscan.c
|
|
* Support routines for scanning Values lists
|
|
* ("VALUES (...), (...), ..." in rangetable).
|
|
*
|
|
* Portions Copyright (c) 1996-2018, PostgreSQL Global Development Group
|
|
* Portions Copyright (c) 1994, Regents of the University of California
|
|
*
|
|
*
|
|
* IDENTIFICATION
|
|
* src/backend/executor/nodeValuesscan.c
|
|
*
|
|
*-------------------------------------------------------------------------
|
|
*/
|
|
/*
|
|
* INTERFACE ROUTINES
|
|
* ExecValuesScan scans a values list.
|
|
* ExecValuesNext retrieve next tuple in sequential order.
|
|
* ExecInitValuesScan creates and initializes a valuesscan node.
|
|
* ExecEndValuesScan releases any storage allocated.
|
|
* ExecReScanValuesScan rescans the values list
|
|
*/
|
|
#include "postgres.h"
|
|
|
|
#include "executor/executor.h"
|
|
#include "executor/nodeValuesscan.h"
|
|
#include "jit/jit.h"
|
|
#include "utils/expandeddatum.h"
|
|
|
|
|
|
static TupleTableSlot *ValuesNext(ValuesScanState *node);
|
|
|
|
|
|
/* ----------------------------------------------------------------
|
|
* Scan Support
|
|
* ----------------------------------------------------------------
|
|
*/
|
|
|
|
/* ----------------------------------------------------------------
|
|
* ValuesNext
|
|
*
|
|
* This is a workhorse for ExecValuesScan
|
|
* ----------------------------------------------------------------
|
|
*/
|
|
static TupleTableSlot *
|
|
ValuesNext(ValuesScanState *node)
|
|
{
|
|
TupleTableSlot *slot;
|
|
EState *estate;
|
|
ExprContext *econtext;
|
|
ScanDirection direction;
|
|
List *exprlist;
|
|
|
|
/*
|
|
* get information from the estate and scan state
|
|
*/
|
|
estate = node->ss.ps.state;
|
|
direction = estate->es_direction;
|
|
slot = node->ss.ss_ScanTupleSlot;
|
|
econtext = node->rowcontext;
|
|
|
|
/*
|
|
* Get the next tuple. Return NULL if no more tuples.
|
|
*/
|
|
if (ScanDirectionIsForward(direction))
|
|
{
|
|
if (node->curr_idx < node->array_len)
|
|
node->curr_idx++;
|
|
if (node->curr_idx < node->array_len)
|
|
exprlist = node->exprlists[node->curr_idx];
|
|
else
|
|
exprlist = NIL;
|
|
}
|
|
else
|
|
{
|
|
if (node->curr_idx >= 0)
|
|
node->curr_idx--;
|
|
if (node->curr_idx >= 0)
|
|
exprlist = node->exprlists[node->curr_idx];
|
|
else
|
|
exprlist = NIL;
|
|
}
|
|
|
|
/*
|
|
* Always clear the result slot; this is appropriate if we are at the end
|
|
* of the data, and if we're not, we still need it as the first step of
|
|
* the store-virtual-tuple protocol. It seems wise to clear the slot
|
|
* before we reset the context it might have pointers into.
|
|
*/
|
|
ExecClearTuple(slot);
|
|
|
|
if (exprlist)
|
|
{
|
|
MemoryContext oldContext;
|
|
List *oldsubplans;
|
|
List *exprstatelist;
|
|
Datum *values;
|
|
bool *isnull;
|
|
ListCell *lc;
|
|
int resind;
|
|
int saved_jit_flags;
|
|
|
|
/*
|
|
* Get rid of any prior cycle's leftovers. We use ReScanExprContext
|
|
* not just ResetExprContext because we want any registered shutdown
|
|
* callbacks to be called.
|
|
*/
|
|
ReScanExprContext(econtext);
|
|
|
|
/*
|
|
* Build the expression eval state in the econtext's per-tuple memory.
|
|
* This is a tad unusual, but we want to delete the eval state again
|
|
* when we move to the next row, to avoid growth of memory
|
|
* requirements over a long values list.
|
|
*/
|
|
oldContext = MemoryContextSwitchTo(econtext->ecxt_per_tuple_memory);
|
|
|
|
/*
|
|
* The expressions might contain SubPlans (this is currently only
|
|
* possible if there's a sub-select containing a LATERAL reference,
|
|
* otherwise sub-selects in a VALUES list should be InitPlans). Those
|
|
* subplans will want to hook themselves into our subPlan list, which
|
|
* would result in a corrupted list after we delete the eval state. We
|
|
* can work around this by saving and restoring the subPlan list.
|
|
* (There's no need for the functionality that would be enabled by
|
|
* having the list entries, since the SubPlans aren't going to be
|
|
* re-executed anyway.)
|
|
*/
|
|
oldsubplans = node->ss.ps.subPlan;
|
|
node->ss.ps.subPlan = NIL;
|
|
|
|
/*
|
|
* As the expressions are only ever used once, disable JIT for them.
|
|
* This is worthwhile because it's common to insert significant
|
|
* amounts of data via VALUES().
|
|
*/
|
|
saved_jit_flags = econtext->ecxt_estate->es_jit_flags;
|
|
econtext->ecxt_estate->es_jit_flags = PGJIT_NONE;
|
|
exprstatelist = ExecInitExprList(exprlist, &node->ss.ps);
|
|
econtext->ecxt_estate->es_jit_flags = saved_jit_flags;
|
|
|
|
node->ss.ps.subPlan = oldsubplans;
|
|
|
|
/* parser should have checked all sublists are the same length */
|
|
Assert(list_length(exprstatelist) == slot->tts_tupleDescriptor->natts);
|
|
|
|
/*
|
|
* Compute the expressions and build a virtual result tuple. We
|
|
* already did ExecClearTuple(slot).
|
|
*/
|
|
values = slot->tts_values;
|
|
isnull = slot->tts_isnull;
|
|
|
|
resind = 0;
|
|
foreach(lc, exprstatelist)
|
|
{
|
|
ExprState *estate = (ExprState *) lfirst(lc);
|
|
Form_pg_attribute attr = TupleDescAttr(slot->tts_tupleDescriptor,
|
|
resind);
|
|
|
|
values[resind] = ExecEvalExpr(estate,
|
|
econtext,
|
|
&isnull[resind]);
|
|
|
|
/*
|
|
* We must force any R/W expanded datums to read-only state, in
|
|
* case they are multiply referenced in the plan node's output
|
|
* expressions, or in case we skip the output projection and the
|
|
* output column is multiply referenced in higher plan nodes.
|
|
*/
|
|
values[resind] = MakeExpandedObjectReadOnly(values[resind],
|
|
isnull[resind],
|
|
attr->attlen);
|
|
|
|
resind++;
|
|
}
|
|
|
|
MemoryContextSwitchTo(oldContext);
|
|
|
|
/*
|
|
* And return the virtual tuple.
|
|
*/
|
|
ExecStoreVirtualTuple(slot);
|
|
}
|
|
|
|
return slot;
|
|
}
|
|
|
|
/*
|
|
* ValuesRecheck -- access method routine to recheck a tuple in EvalPlanQual
|
|
*/
|
|
static bool
|
|
ValuesRecheck(ValuesScanState *node, TupleTableSlot *slot)
|
|
{
|
|
/* nothing to check */
|
|
return true;
|
|
}
|
|
|
|
/* ----------------------------------------------------------------
|
|
* ExecValuesScan(node)
|
|
*
|
|
* Scans the values lists sequentially and returns the next qualifying
|
|
* tuple.
|
|
* We call the ExecScan() routine and pass it the appropriate
|
|
* access method functions.
|
|
* ----------------------------------------------------------------
|
|
*/
|
|
static TupleTableSlot *
|
|
ExecValuesScan(PlanState *pstate)
|
|
{
|
|
ValuesScanState *node = castNode(ValuesScanState, pstate);
|
|
|
|
return ExecScan(&node->ss,
|
|
(ExecScanAccessMtd) ValuesNext,
|
|
(ExecScanRecheckMtd) ValuesRecheck);
|
|
}
|
|
|
|
/* ----------------------------------------------------------------
|
|
* ExecInitValuesScan
|
|
* ----------------------------------------------------------------
|
|
*/
|
|
ValuesScanState *
|
|
ExecInitValuesScan(ValuesScan *node, EState *estate, int eflags)
|
|
{
|
|
ValuesScanState *scanstate;
|
|
TupleDesc tupdesc;
|
|
ListCell *vtl;
|
|
int i;
|
|
PlanState *planstate;
|
|
|
|
/*
|
|
* ValuesScan should not have any children.
|
|
*/
|
|
Assert(outerPlan(node) == NULL);
|
|
Assert(innerPlan(node) == NULL);
|
|
|
|
/*
|
|
* create new ScanState for node
|
|
*/
|
|
scanstate = makeNode(ValuesScanState);
|
|
scanstate->ss.ps.plan = (Plan *) node;
|
|
scanstate->ss.ps.state = estate;
|
|
scanstate->ss.ps.ExecProcNode = ExecValuesScan;
|
|
|
|
/*
|
|
* Miscellaneous initialization
|
|
*/
|
|
planstate = &scanstate->ss.ps;
|
|
|
|
/*
|
|
* Create expression contexts. We need two, one for per-sublist
|
|
* processing and one for execScan.c to use for quals and projections. We
|
|
* cheat a little by using ExecAssignExprContext() to build both.
|
|
*/
|
|
ExecAssignExprContext(estate, planstate);
|
|
scanstate->rowcontext = planstate->ps_ExprContext;
|
|
ExecAssignExprContext(estate, planstate);
|
|
|
|
/*
|
|
* Get info about values list, initialize scan slot with it.
|
|
*/
|
|
tupdesc = ExecTypeFromExprList((List *) linitial(node->values_lists));
|
|
ExecInitScanTupleSlot(estate, &scanstate->ss, tupdesc);
|
|
|
|
/*
|
|
* Initialize result slot, type and projection.
|
|
*/
|
|
ExecInitResultTupleSlotTL(estate, &scanstate->ss.ps);
|
|
ExecAssignScanProjectionInfo(&scanstate->ss);
|
|
|
|
/*
|
|
* initialize child expressions
|
|
*/
|
|
scanstate->ss.ps.qual =
|
|
ExecInitQual(node->scan.plan.qual, (PlanState *) scanstate);
|
|
|
|
/*
|
|
* Other node-specific setup
|
|
*/
|
|
scanstate->curr_idx = -1;
|
|
scanstate->array_len = list_length(node->values_lists);
|
|
|
|
/* convert list of sublists into array of sublists for easy addressing */
|
|
scanstate->exprlists = (List **)
|
|
palloc(scanstate->array_len * sizeof(List *));
|
|
i = 0;
|
|
foreach(vtl, node->values_lists)
|
|
{
|
|
scanstate->exprlists[i++] = (List *) lfirst(vtl);
|
|
}
|
|
|
|
return scanstate;
|
|
}
|
|
|
|
/* ----------------------------------------------------------------
|
|
* ExecEndValuesScan
|
|
*
|
|
* frees any storage allocated through C routines.
|
|
* ----------------------------------------------------------------
|
|
*/
|
|
void
|
|
ExecEndValuesScan(ValuesScanState *node)
|
|
{
|
|
/*
|
|
* Free both exprcontexts
|
|
*/
|
|
ExecFreeExprContext(&node->ss.ps);
|
|
node->ss.ps.ps_ExprContext = node->rowcontext;
|
|
ExecFreeExprContext(&node->ss.ps);
|
|
|
|
/*
|
|
* clean out the tuple table
|
|
*/
|
|
ExecClearTuple(node->ss.ps.ps_ResultTupleSlot);
|
|
ExecClearTuple(node->ss.ss_ScanTupleSlot);
|
|
}
|
|
|
|
/* ----------------------------------------------------------------
|
|
* ExecReScanValuesScan
|
|
*
|
|
* Rescans the relation.
|
|
* ----------------------------------------------------------------
|
|
*/
|
|
void
|
|
ExecReScanValuesScan(ValuesScanState *node)
|
|
{
|
|
ExecClearTuple(node->ss.ps.ps_ResultTupleSlot);
|
|
|
|
ExecScanReScan(&node->ss);
|
|
|
|
node->curr_idx = -1;
|
|
}
|