mirror of
https://github.com/postgres/postgres.git
synced 2025-06-30 21:42:05 +03:00
Add support for multi-row VALUES clauses as part of INSERT statements
(e.g. "INSERT ... VALUES (...), (...), ...") and elsewhere as allowed by the spec. (e.g. similar to a FROM clause subselect). initdb required. Joe Conway and Tom Lane.
This commit is contained in:
@ -4,7 +4,7 @@
|
||||
# Makefile for executor
|
||||
#
|
||||
# IDENTIFICATION
|
||||
# $PostgreSQL: pgsql/src/backend/executor/Makefile,v 1.23 2005/04/19 22:35:11 tgl Exp $
|
||||
# $PostgreSQL: pgsql/src/backend/executor/Makefile,v 1.24 2006/08/02 01:59:45 joe Exp $
|
||||
#
|
||||
#-------------------------------------------------------------------------
|
||||
|
||||
@ -19,7 +19,8 @@ OBJS = execAmi.o execGrouping.o execJunk.o execMain.o \
|
||||
nodeBitmapHeapscan.o nodeBitmapIndexscan.o nodeHash.o \
|
||||
nodeHashjoin.o nodeIndexscan.o nodeMaterial.o nodeMergejoin.o \
|
||||
nodeNestloop.o nodeFunctionscan.o nodeResult.o nodeSeqscan.o \
|
||||
nodeSetOp.o nodeSort.o nodeUnique.o nodeLimit.o nodeGroup.o \
|
||||
nodeSetOp.o nodeSort.o nodeUnique.o \
|
||||
nodeValuesscan.o nodeLimit.o nodeGroup.o \
|
||||
nodeSubplan.o nodeSubqueryscan.o nodeTidscan.o tstoreReceiver.o spi.o
|
||||
|
||||
all: SUBSYS.o
|
||||
|
@ -6,7 +6,7 @@
|
||||
* Portions Copyright (c) 1996-2006, PostgreSQL Global Development Group
|
||||
* Portions Copyright (c) 1994, Regents of the University of California
|
||||
*
|
||||
* $PostgreSQL: pgsql/src/backend/executor/execAmi.c,v 1.88 2006/07/14 14:52:18 momjian Exp $
|
||||
* $PostgreSQL: pgsql/src/backend/executor/execAmi.c,v 1.89 2006/08/02 01:59:45 joe Exp $
|
||||
*
|
||||
*-------------------------------------------------------------------------
|
||||
*/
|
||||
@ -38,6 +38,7 @@
|
||||
#include "executor/nodeSubqueryscan.h"
|
||||
#include "executor/nodeTidscan.h"
|
||||
#include "executor/nodeUnique.h"
|
||||
#include "executor/nodeValuesscan.h"
|
||||
|
||||
|
||||
/*
|
||||
@ -144,6 +145,10 @@ ExecReScan(PlanState *node, ExprContext *exprCtxt)
|
||||
ExecFunctionReScan((FunctionScanState *) node, exprCtxt);
|
||||
break;
|
||||
|
||||
case T_ValuesScanState:
|
||||
ExecValuesReScan((ValuesScanState *) node, exprCtxt);
|
||||
break;
|
||||
|
||||
case T_NestLoopState:
|
||||
ExecReScanNestLoop((NestLoopState *) node, exprCtxt);
|
||||
break;
|
||||
@ -226,6 +231,10 @@ ExecMarkPos(PlanState *node)
|
||||
ExecFunctionMarkPos((FunctionScanState *) node);
|
||||
break;
|
||||
|
||||
case T_ValuesScanState:
|
||||
ExecValuesMarkPos((ValuesScanState *) node);
|
||||
break;
|
||||
|
||||
case T_MaterialState:
|
||||
ExecMaterialMarkPos((MaterialState *) node);
|
||||
break;
|
||||
@ -275,6 +284,10 @@ ExecRestrPos(PlanState *node)
|
||||
ExecFunctionRestrPos((FunctionScanState *) node);
|
||||
break;
|
||||
|
||||
case T_ValuesScanState:
|
||||
ExecValuesRestrPos((ValuesScanState *) node);
|
||||
break;
|
||||
|
||||
case T_MaterialState:
|
||||
ExecMaterialRestrPos((MaterialState *) node);
|
||||
break;
|
||||
@ -298,8 +311,8 @@ ExecRestrPos(PlanState *node)
|
||||
*
|
||||
* (However, since the only present use of mark/restore is in mergejoin,
|
||||
* there is no need to support mark/restore in any plan type that is not
|
||||
* capable of generating ordered output. So the seqscan, tidscan, and
|
||||
* functionscan support is actually useless code at present.)
|
||||
* capable of generating ordered output. So the seqscan, tidscan,
|
||||
* functionscan, and valuesscan support is actually useless code at present.)
|
||||
*/
|
||||
bool
|
||||
ExecSupportsMarkRestore(NodeTag plantype)
|
||||
@ -310,6 +323,7 @@ ExecSupportsMarkRestore(NodeTag plantype)
|
||||
case T_IndexScan:
|
||||
case T_TidScan:
|
||||
case T_FunctionScan:
|
||||
case T_ValuesScan:
|
||||
case T_Material:
|
||||
case T_Sort:
|
||||
return true;
|
||||
@ -359,6 +373,7 @@ ExecSupportsBackwardScan(Plan *node)
|
||||
case T_IndexScan:
|
||||
case T_TidScan:
|
||||
case T_FunctionScan:
|
||||
case T_ValuesScan:
|
||||
return true;
|
||||
|
||||
case T_SubqueryScan:
|
||||
@ -413,6 +428,7 @@ ExecMayReturnRawTuples(PlanState *node)
|
||||
case T_TidScanState:
|
||||
case T_SubqueryScanState:
|
||||
case T_FunctionScanState:
|
||||
case T_ValuesScanState:
|
||||
if (node->ps_ProjInfo == NULL)
|
||||
return true;
|
||||
break;
|
||||
|
@ -12,7 +12,7 @@
|
||||
*
|
||||
*
|
||||
* IDENTIFICATION
|
||||
* $PostgreSQL: pgsql/src/backend/executor/execProcnode.c,v 1.57 2006/07/14 14:52:18 momjian Exp $
|
||||
* $PostgreSQL: pgsql/src/backend/executor/execProcnode.c,v 1.58 2006/08/02 01:59:45 joe Exp $
|
||||
*
|
||||
*-------------------------------------------------------------------------
|
||||
*/
|
||||
@ -102,6 +102,7 @@
|
||||
#include "executor/nodeSubqueryscan.h"
|
||||
#include "executor/nodeTidscan.h"
|
||||
#include "executor/nodeUnique.h"
|
||||
#include "executor/nodeValuesscan.h"
|
||||
#include "miscadmin.h"
|
||||
|
||||
/* ------------------------------------------------------------------------
|
||||
@ -194,6 +195,11 @@ ExecInitNode(Plan *node, EState *estate, int eflags)
|
||||
estate, eflags);
|
||||
break;
|
||||
|
||||
case T_ValuesScan:
|
||||
result = (PlanState *) ExecInitValuesScan((ValuesScan *) node,
|
||||
estate, eflags);
|
||||
break;
|
||||
|
||||
/*
|
||||
* join nodes
|
||||
*/
|
||||
@ -365,6 +371,10 @@ ExecProcNode(PlanState *node)
|
||||
result = ExecFunctionScan((FunctionScanState *) node);
|
||||
break;
|
||||
|
||||
case T_ValuesScanState:
|
||||
result = ExecValuesScan((ValuesScanState *) node);
|
||||
break;
|
||||
|
||||
/*
|
||||
* join nodes
|
||||
*/
|
||||
@ -536,6 +546,9 @@ ExecCountSlotsNode(Plan *node)
|
||||
case T_FunctionScan:
|
||||
return ExecCountSlotsFunctionScan((FunctionScan *) node);
|
||||
|
||||
case T_ValuesScan:
|
||||
return ExecCountSlotsValuesScan((ValuesScan *) node);
|
||||
|
||||
/*
|
||||
* join nodes
|
||||
*/
|
||||
@ -669,6 +682,10 @@ ExecEndNode(PlanState *node)
|
||||
ExecEndFunctionScan((FunctionScanState *) node);
|
||||
break;
|
||||
|
||||
case T_ValuesScanState:
|
||||
ExecEndValuesScan((ValuesScanState *) node);
|
||||
break;
|
||||
|
||||
/*
|
||||
* join nodes
|
||||
*/
|
||||
|
332
src/backend/executor/nodeValuesscan.c
Normal file
332
src/backend/executor/nodeValuesscan.c
Normal file
@ -0,0 +1,332 @@
|
||||
/*-------------------------------------------------------------------------
|
||||
*
|
||||
* nodeValuesscan.c
|
||||
* Support routines for scanning Values lists
|
||||
* ("VALUES (...), (...), ..." in rangetable).
|
||||
*
|
||||
* Portions Copyright (c) 1996-2006, PostgreSQL Global Development Group
|
||||
* Portions Copyright (c) 1994, Regents of the University of California
|
||||
*
|
||||
*
|
||||
* IDENTIFICATION
|
||||
* $PostgreSQL: pgsql/src/backend/executor/nodeValuesscan.c,v 1.1 2006/08/02 01:59:45 joe Exp $
|
||||
*
|
||||
*-------------------------------------------------------------------------
|
||||
*/
|
||||
/*
|
||||
* 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.
|
||||
* ExecValuesReScan rescans the values list
|
||||
*/
|
||||
#include "postgres.h"
|
||||
|
||||
#include "executor/executor.h"
|
||||
#include "executor/nodeValuesscan.h"
|
||||
#include "parser/parsetree.h"
|
||||
#include "utils/memutils.h"
|
||||
|
||||
|
||||
static TupleTableSlot *ValuesNext(ValuesScanState *node);
|
||||
static void ExecMakeValuesResult(List *targetlist,
|
||||
ExprContext *econtext,
|
||||
TupleTableSlot *slot);
|
||||
|
||||
|
||||
/* ----------------------------------------------------------------
|
||||
* 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->ss.ps.ps_ExprContext;
|
||||
|
||||
/*
|
||||
* 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;
|
||||
}
|
||||
|
||||
if (exprlist)
|
||||
{
|
||||
List *init_exprlist;
|
||||
|
||||
init_exprlist = (List *) ExecInitExpr((Expr *) exprlist,
|
||||
(PlanState *) node);
|
||||
ExecMakeValuesResult(init_exprlist,
|
||||
econtext,
|
||||
slot);
|
||||
list_free_deep(init_exprlist);
|
||||
}
|
||||
else
|
||||
ExecClearTuple(slot);
|
||||
|
||||
return slot;
|
||||
}
|
||||
|
||||
/*
|
||||
* ExecMakeValuesResult
|
||||
*
|
||||
* Evaluate a values list, store into a virtual slot.
|
||||
*/
|
||||
static void
|
||||
ExecMakeValuesResult(List *targetlist,
|
||||
ExprContext *econtext,
|
||||
TupleTableSlot *slot)
|
||||
{
|
||||
MemoryContext oldContext;
|
||||
Datum *values;
|
||||
bool *isnull;
|
||||
ListCell *lc;
|
||||
int resind = 0;
|
||||
|
||||
/* caller should have checked all targetlists are the same length */
|
||||
Assert(list_length(targetlist) == slot->tts_tupleDescriptor->natts);
|
||||
|
||||
/*
|
||||
* Prepare to build a virtual result tuple.
|
||||
*/
|
||||
ExecClearTuple(slot);
|
||||
values = slot->tts_values;
|
||||
isnull = slot->tts_isnull;
|
||||
|
||||
/*
|
||||
* Switch to short-lived context for evaluating the row.
|
||||
* Reset per-tuple memory context before each row.
|
||||
*/
|
||||
ResetExprContext(econtext);
|
||||
oldContext = MemoryContextSwitchTo(econtext->ecxt_per_tuple_memory);
|
||||
|
||||
foreach(lc, targetlist)
|
||||
{
|
||||
ExprState *estate = (ExprState *) lfirst(lc);
|
||||
|
||||
values[resind] = ExecEvalExpr(estate,
|
||||
econtext,
|
||||
&isnull[resind],
|
||||
NULL);
|
||||
resind++;
|
||||
}
|
||||
|
||||
MemoryContextSwitchTo(oldContext);
|
||||
|
||||
/*
|
||||
* And return the virtual tuple.
|
||||
*/
|
||||
ExecStoreVirtualTuple(slot);
|
||||
}
|
||||
|
||||
|
||||
/* ----------------------------------------------------------------
|
||||
* ExecValuesScan(node)
|
||||
*
|
||||
* Scans the values lists sequentially and returns the next qualifying
|
||||
* tuple.
|
||||
* It calls the ExecScan() routine and passes it the access method
|
||||
* which retrieves tuples sequentially.
|
||||
* ----------------------------------------------------------------
|
||||
*/
|
||||
TupleTableSlot *
|
||||
ExecValuesScan(ValuesScanState *node)
|
||||
{
|
||||
/*
|
||||
* use ValuesNext as access method
|
||||
*/
|
||||
return ExecScan(&node->ss, (ExecScanAccessMtd) ValuesNext);
|
||||
}
|
||||
|
||||
/* ----------------------------------------------------------------
|
||||
* ExecInitValuesScan
|
||||
* ----------------------------------------------------------------
|
||||
*/
|
||||
ValuesScanState *
|
||||
ExecInitValuesScan(ValuesScan *node, EState *estate, int eflags)
|
||||
{
|
||||
ValuesScanState *scanstate;
|
||||
RangeTblEntry *rte;
|
||||
TupleDesc tupdesc;
|
||||
ListCell *vtl;
|
||||
int i;
|
||||
PlanState *planstate;
|
||||
ExprContext *econtext;
|
||||
|
||||
/*
|
||||
* 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;
|
||||
|
||||
/*
|
||||
* Miscellaneous initialization
|
||||
*
|
||||
* create expression context for node
|
||||
*/
|
||||
planstate = &scanstate->ss.ps;
|
||||
ExecAssignExprContext(estate, planstate);
|
||||
econtext = planstate->ps_ExprContext;
|
||||
|
||||
#define VALUESSCAN_NSLOTS 2
|
||||
|
||||
/*
|
||||
* tuple table initialization
|
||||
*/
|
||||
ExecInitResultTupleSlot(estate, &scanstate->ss.ps);
|
||||
ExecInitScanTupleSlot(estate, &scanstate->ss);
|
||||
|
||||
/*
|
||||
* initialize child expressions
|
||||
*/
|
||||
scanstate->ss.ps.targetlist = (List *)
|
||||
ExecInitExpr((Expr *) node->scan.plan.targetlist,
|
||||
(PlanState *) scanstate);
|
||||
scanstate->ss.ps.qual = (List *)
|
||||
ExecInitExpr((Expr *) node->scan.plan.qual,
|
||||
(PlanState *) scanstate);
|
||||
|
||||
/*
|
||||
* get info about values list
|
||||
*/
|
||||
rte = rt_fetch(node->scan.scanrelid, estate->es_range_table);
|
||||
Assert(rte->rtekind == RTE_VALUES);
|
||||
tupdesc = ExecTypeFromExprList((List *) linitial(rte->values_lists));
|
||||
|
||||
ExecAssignScanType(&scanstate->ss, tupdesc);
|
||||
|
||||
/*
|
||||
* Other node-specific setup
|
||||
*/
|
||||
scanstate->marked_idx = -1;
|
||||
scanstate->curr_idx = -1;
|
||||
scanstate->array_len = list_length(rte->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, rte->values_lists)
|
||||
{
|
||||
scanstate->exprlists[i++] = (List *) lfirst(vtl);
|
||||
}
|
||||
|
||||
scanstate->ss.ps.ps_TupFromTlist = false;
|
||||
|
||||
/*
|
||||
* Initialize result tuple type and projection info.
|
||||
*/
|
||||
ExecAssignResultTypeFromTL(&scanstate->ss.ps);
|
||||
ExecAssignScanProjectionInfo(&scanstate->ss);
|
||||
|
||||
return scanstate;
|
||||
}
|
||||
|
||||
int
|
||||
ExecCountSlotsValuesScan(ValuesScan *node)
|
||||
{
|
||||
return ExecCountSlotsNode(outerPlan(node)) +
|
||||
ExecCountSlotsNode(innerPlan(node)) +
|
||||
VALUESSCAN_NSLOTS;
|
||||
}
|
||||
|
||||
/* ----------------------------------------------------------------
|
||||
* ExecEndValuesScan
|
||||
*
|
||||
* frees any storage allocated through C routines.
|
||||
* ----------------------------------------------------------------
|
||||
*/
|
||||
void
|
||||
ExecEndValuesScan(ValuesScanState *node)
|
||||
{
|
||||
/*
|
||||
* Free the exprcontext
|
||||
*/
|
||||
ExecFreeExprContext(&node->ss.ps);
|
||||
|
||||
/*
|
||||
* clean out the tuple table
|
||||
*/
|
||||
ExecClearTuple(node->ss.ps.ps_ResultTupleSlot);
|
||||
ExecClearTuple(node->ss.ss_ScanTupleSlot);
|
||||
}
|
||||
|
||||
/* ----------------------------------------------------------------
|
||||
* ExecValuesMarkPos
|
||||
*
|
||||
* Marks scan position.
|
||||
* ----------------------------------------------------------------
|
||||
*/
|
||||
void
|
||||
ExecValuesMarkPos(ValuesScanState *node)
|
||||
{
|
||||
node->marked_idx = node->curr_idx;
|
||||
}
|
||||
|
||||
/* ----------------------------------------------------------------
|
||||
* ExecValuesRestrPos
|
||||
*
|
||||
* Restores scan position.
|
||||
* ----------------------------------------------------------------
|
||||
*/
|
||||
void
|
||||
ExecValuesRestrPos(ValuesScanState *node)
|
||||
{
|
||||
node->curr_idx = node->marked_idx;
|
||||
}
|
||||
|
||||
/* ----------------------------------------------------------------
|
||||
* ExecValuesReScan
|
||||
*
|
||||
* Rescans the relation.
|
||||
* ----------------------------------------------------------------
|
||||
*/
|
||||
void
|
||||
ExecValuesReScan(ValuesScanState *node, ExprContext *exprCtxt)
|
||||
{
|
||||
ExecClearTuple(node->ss.ps.ps_ResultTupleSlot);
|
||||
|
||||
node->curr_idx = -1;
|
||||
}
|
Reference in New Issue
Block a user