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:
parent
d307c428cb
commit
9caafda579
@ -8,7 +8,7 @@
|
|||||||
*
|
*
|
||||||
*
|
*
|
||||||
* IDENTIFICATION
|
* IDENTIFICATION
|
||||||
* $PostgreSQL: pgsql/src/backend/catalog/heap.c,v 1.311 2006/07/31 20:09:00 tgl Exp $
|
* $PostgreSQL: pgsql/src/backend/catalog/heap.c,v 1.312 2006/08/02 01:59:44 joe Exp $
|
||||||
*
|
*
|
||||||
*
|
*
|
||||||
* INTERFACE ROUTINES
|
* INTERFACE ROUTINES
|
||||||
@ -1841,7 +1841,7 @@ cookDefault(ParseState *pstate,
|
|||||||
/*
|
/*
|
||||||
* Coerce the expression to the correct type and typmod, if given. This
|
* Coerce the expression to the correct type and typmod, if given. This
|
||||||
* should match the parser's processing of non-defaulted expressions ---
|
* should match the parser's processing of non-defaulted expressions ---
|
||||||
* see updateTargetListEntry().
|
* see transformAssignedExpr().
|
||||||
*/
|
*/
|
||||||
if (OidIsValid(atttypid))
|
if (OidIsValid(atttypid))
|
||||||
{
|
{
|
||||||
|
@ -7,7 +7,7 @@
|
|||||||
* Portions Copyright (c) 1994-5, Regents of the University of California
|
* Portions Copyright (c) 1994-5, Regents of the University of California
|
||||||
*
|
*
|
||||||
* IDENTIFICATION
|
* IDENTIFICATION
|
||||||
* $PostgreSQL: pgsql/src/backend/commands/explain.c,v 1.149 2006/07/14 14:52:18 momjian Exp $
|
* $PostgreSQL: pgsql/src/backend/commands/explain.c,v 1.150 2006/08/02 01:59:45 joe Exp $
|
||||||
*
|
*
|
||||||
*-------------------------------------------------------------------------
|
*-------------------------------------------------------------------------
|
||||||
*/
|
*/
|
||||||
@ -527,6 +527,9 @@ explain_outNode(StringInfo str,
|
|||||||
case T_FunctionScan:
|
case T_FunctionScan:
|
||||||
pname = "Function Scan";
|
pname = "Function Scan";
|
||||||
break;
|
break;
|
||||||
|
case T_ValuesScan:
|
||||||
|
pname = "Values Scan";
|
||||||
|
break;
|
||||||
case T_Material:
|
case T_Material:
|
||||||
pname = "Materialize";
|
pname = "Materialize";
|
||||||
break;
|
break;
|
||||||
@ -666,6 +669,22 @@ explain_outNode(StringInfo str,
|
|||||||
quote_identifier(rte->eref->aliasname));
|
quote_identifier(rte->eref->aliasname));
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
|
case T_ValuesScan:
|
||||||
|
if (((Scan *) plan)->scanrelid > 0)
|
||||||
|
{
|
||||||
|
RangeTblEntry *rte = rt_fetch(((Scan *) plan)->scanrelid,
|
||||||
|
es->rtable);
|
||||||
|
char *valsname;
|
||||||
|
|
||||||
|
/* Assert it's on a values rte */
|
||||||
|
Assert(rte->rtekind == RTE_VALUES);
|
||||||
|
|
||||||
|
valsname = rte->eref->aliasname;
|
||||||
|
|
||||||
|
appendStringInfo(str, " on %s",
|
||||||
|
quote_identifier(valsname));
|
||||||
|
}
|
||||||
|
break;
|
||||||
default:
|
default:
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
@ -728,6 +747,7 @@ explain_outNode(StringInfo str,
|
|||||||
case T_SeqScan:
|
case T_SeqScan:
|
||||||
case T_SubqueryScan:
|
case T_SubqueryScan:
|
||||||
case T_FunctionScan:
|
case T_FunctionScan:
|
||||||
|
case T_ValuesScan:
|
||||||
show_scan_qual(plan->qual,
|
show_scan_qual(plan->qual,
|
||||||
"Filter",
|
"Filter",
|
||||||
((Scan *) plan)->scanrelid,
|
((Scan *) plan)->scanrelid,
|
||||||
|
@ -4,7 +4,7 @@
|
|||||||
# Makefile for executor
|
# Makefile for executor
|
||||||
#
|
#
|
||||||
# IDENTIFICATION
|
# 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 \
|
nodeBitmapHeapscan.o nodeBitmapIndexscan.o nodeHash.o \
|
||||||
nodeHashjoin.o nodeIndexscan.o nodeMaterial.o nodeMergejoin.o \
|
nodeHashjoin.o nodeIndexscan.o nodeMaterial.o nodeMergejoin.o \
|
||||||
nodeNestloop.o nodeFunctionscan.o nodeResult.o nodeSeqscan.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
|
nodeSubplan.o nodeSubqueryscan.o nodeTidscan.o tstoreReceiver.o spi.o
|
||||||
|
|
||||||
all: SUBSYS.o
|
all: SUBSYS.o
|
||||||
|
@ -6,7 +6,7 @@
|
|||||||
* Portions Copyright (c) 1996-2006, PostgreSQL Global Development Group
|
* Portions Copyright (c) 1996-2006, PostgreSQL Global Development Group
|
||||||
* Portions Copyright (c) 1994, Regents of the University of California
|
* 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/nodeSubqueryscan.h"
|
||||||
#include "executor/nodeTidscan.h"
|
#include "executor/nodeTidscan.h"
|
||||||
#include "executor/nodeUnique.h"
|
#include "executor/nodeUnique.h"
|
||||||
|
#include "executor/nodeValuesscan.h"
|
||||||
|
|
||||||
|
|
||||||
/*
|
/*
|
||||||
@ -144,6 +145,10 @@ ExecReScan(PlanState *node, ExprContext *exprCtxt)
|
|||||||
ExecFunctionReScan((FunctionScanState *) node, exprCtxt);
|
ExecFunctionReScan((FunctionScanState *) node, exprCtxt);
|
||||||
break;
|
break;
|
||||||
|
|
||||||
|
case T_ValuesScanState:
|
||||||
|
ExecValuesReScan((ValuesScanState *) node, exprCtxt);
|
||||||
|
break;
|
||||||
|
|
||||||
case T_NestLoopState:
|
case T_NestLoopState:
|
||||||
ExecReScanNestLoop((NestLoopState *) node, exprCtxt);
|
ExecReScanNestLoop((NestLoopState *) node, exprCtxt);
|
||||||
break;
|
break;
|
||||||
@ -226,6 +231,10 @@ ExecMarkPos(PlanState *node)
|
|||||||
ExecFunctionMarkPos((FunctionScanState *) node);
|
ExecFunctionMarkPos((FunctionScanState *) node);
|
||||||
break;
|
break;
|
||||||
|
|
||||||
|
case T_ValuesScanState:
|
||||||
|
ExecValuesMarkPos((ValuesScanState *) node);
|
||||||
|
break;
|
||||||
|
|
||||||
case T_MaterialState:
|
case T_MaterialState:
|
||||||
ExecMaterialMarkPos((MaterialState *) node);
|
ExecMaterialMarkPos((MaterialState *) node);
|
||||||
break;
|
break;
|
||||||
@ -275,6 +284,10 @@ ExecRestrPos(PlanState *node)
|
|||||||
ExecFunctionRestrPos((FunctionScanState *) node);
|
ExecFunctionRestrPos((FunctionScanState *) node);
|
||||||
break;
|
break;
|
||||||
|
|
||||||
|
case T_ValuesScanState:
|
||||||
|
ExecValuesRestrPos((ValuesScanState *) node);
|
||||||
|
break;
|
||||||
|
|
||||||
case T_MaterialState:
|
case T_MaterialState:
|
||||||
ExecMaterialRestrPos((MaterialState *) node);
|
ExecMaterialRestrPos((MaterialState *) node);
|
||||||
break;
|
break;
|
||||||
@ -298,8 +311,8 @@ ExecRestrPos(PlanState *node)
|
|||||||
*
|
*
|
||||||
* (However, since the only present use of mark/restore is in mergejoin,
|
* (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
|
* 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
|
* capable of generating ordered output. So the seqscan, tidscan,
|
||||||
* functionscan support is actually useless code at present.)
|
* functionscan, and valuesscan support is actually useless code at present.)
|
||||||
*/
|
*/
|
||||||
bool
|
bool
|
||||||
ExecSupportsMarkRestore(NodeTag plantype)
|
ExecSupportsMarkRestore(NodeTag plantype)
|
||||||
@ -310,6 +323,7 @@ ExecSupportsMarkRestore(NodeTag plantype)
|
|||||||
case T_IndexScan:
|
case T_IndexScan:
|
||||||
case T_TidScan:
|
case T_TidScan:
|
||||||
case T_FunctionScan:
|
case T_FunctionScan:
|
||||||
|
case T_ValuesScan:
|
||||||
case T_Material:
|
case T_Material:
|
||||||
case T_Sort:
|
case T_Sort:
|
||||||
return true;
|
return true;
|
||||||
@ -359,6 +373,7 @@ ExecSupportsBackwardScan(Plan *node)
|
|||||||
case T_IndexScan:
|
case T_IndexScan:
|
||||||
case T_TidScan:
|
case T_TidScan:
|
||||||
case T_FunctionScan:
|
case T_FunctionScan:
|
||||||
|
case T_ValuesScan:
|
||||||
return true;
|
return true;
|
||||||
|
|
||||||
case T_SubqueryScan:
|
case T_SubqueryScan:
|
||||||
@ -413,6 +428,7 @@ ExecMayReturnRawTuples(PlanState *node)
|
|||||||
case T_TidScanState:
|
case T_TidScanState:
|
||||||
case T_SubqueryScanState:
|
case T_SubqueryScanState:
|
||||||
case T_FunctionScanState:
|
case T_FunctionScanState:
|
||||||
|
case T_ValuesScanState:
|
||||||
if (node->ps_ProjInfo == NULL)
|
if (node->ps_ProjInfo == NULL)
|
||||||
return true;
|
return true;
|
||||||
break;
|
break;
|
||||||
|
@ -12,7 +12,7 @@
|
|||||||
*
|
*
|
||||||
*
|
*
|
||||||
* IDENTIFICATION
|
* 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/nodeSubqueryscan.h"
|
||||||
#include "executor/nodeTidscan.h"
|
#include "executor/nodeTidscan.h"
|
||||||
#include "executor/nodeUnique.h"
|
#include "executor/nodeUnique.h"
|
||||||
|
#include "executor/nodeValuesscan.h"
|
||||||
#include "miscadmin.h"
|
#include "miscadmin.h"
|
||||||
|
|
||||||
/* ------------------------------------------------------------------------
|
/* ------------------------------------------------------------------------
|
||||||
@ -194,6 +195,11 @@ ExecInitNode(Plan *node, EState *estate, int eflags)
|
|||||||
estate, eflags);
|
estate, eflags);
|
||||||
break;
|
break;
|
||||||
|
|
||||||
|
case T_ValuesScan:
|
||||||
|
result = (PlanState *) ExecInitValuesScan((ValuesScan *) node,
|
||||||
|
estate, eflags);
|
||||||
|
break;
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* join nodes
|
* join nodes
|
||||||
*/
|
*/
|
||||||
@ -365,6 +371,10 @@ ExecProcNode(PlanState *node)
|
|||||||
result = ExecFunctionScan((FunctionScanState *) node);
|
result = ExecFunctionScan((FunctionScanState *) node);
|
||||||
break;
|
break;
|
||||||
|
|
||||||
|
case T_ValuesScanState:
|
||||||
|
result = ExecValuesScan((ValuesScanState *) node);
|
||||||
|
break;
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* join nodes
|
* join nodes
|
||||||
*/
|
*/
|
||||||
@ -536,6 +546,9 @@ ExecCountSlotsNode(Plan *node)
|
|||||||
case T_FunctionScan:
|
case T_FunctionScan:
|
||||||
return ExecCountSlotsFunctionScan((FunctionScan *) node);
|
return ExecCountSlotsFunctionScan((FunctionScan *) node);
|
||||||
|
|
||||||
|
case T_ValuesScan:
|
||||||
|
return ExecCountSlotsValuesScan((ValuesScan *) node);
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* join nodes
|
* join nodes
|
||||||
*/
|
*/
|
||||||
@ -669,6 +682,10 @@ ExecEndNode(PlanState *node)
|
|||||||
ExecEndFunctionScan((FunctionScanState *) node);
|
ExecEndFunctionScan((FunctionScanState *) node);
|
||||||
break;
|
break;
|
||||||
|
|
||||||
|
case T_ValuesScanState:
|
||||||
|
ExecEndValuesScan((ValuesScanState *) node);
|
||||||
|
break;
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* join nodes
|
* 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;
|
||||||
|
}
|
@ -15,7 +15,7 @@
|
|||||||
* Portions Copyright (c) 1994, Regents of the University of California
|
* Portions Copyright (c) 1994, Regents of the University of California
|
||||||
*
|
*
|
||||||
* IDENTIFICATION
|
* IDENTIFICATION
|
||||||
* $PostgreSQL: pgsql/src/backend/nodes/copyfuncs.c,v 1.344 2006/07/27 19:52:05 tgl Exp $
|
* $PostgreSQL: pgsql/src/backend/nodes/copyfuncs.c,v 1.345 2006/08/02 01:59:45 joe Exp $
|
||||||
*
|
*
|
||||||
*-------------------------------------------------------------------------
|
*-------------------------------------------------------------------------
|
||||||
*/
|
*/
|
||||||
@ -366,6 +366,22 @@ _copyFunctionScan(FunctionScan *from)
|
|||||||
return newnode;
|
return newnode;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* _copyValuesScan
|
||||||
|
*/
|
||||||
|
static ValuesScan *
|
||||||
|
_copyValuesScan(ValuesScan *from)
|
||||||
|
{
|
||||||
|
ValuesScan *newnode = makeNode(ValuesScan);
|
||||||
|
|
||||||
|
/*
|
||||||
|
* copy node superclass fields
|
||||||
|
*/
|
||||||
|
CopyScanFields((Scan *) from, (Scan *) newnode);
|
||||||
|
|
||||||
|
return newnode;
|
||||||
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* CopyJoinFields
|
* CopyJoinFields
|
||||||
*
|
*
|
||||||
@ -1356,6 +1372,7 @@ _copyRangeTblEntry(RangeTblEntry *from)
|
|||||||
COPY_NODE_FIELD(funcexpr);
|
COPY_NODE_FIELD(funcexpr);
|
||||||
COPY_NODE_FIELD(funccoltypes);
|
COPY_NODE_FIELD(funccoltypes);
|
||||||
COPY_NODE_FIELD(funccoltypmods);
|
COPY_NODE_FIELD(funccoltypmods);
|
||||||
|
COPY_NODE_FIELD(values_lists);
|
||||||
COPY_SCALAR_FIELD(jointype);
|
COPY_SCALAR_FIELD(jointype);
|
||||||
COPY_NODE_FIELD(joinaliasvars);
|
COPY_NODE_FIELD(joinaliasvars);
|
||||||
COPY_NODE_FIELD(alias);
|
COPY_NODE_FIELD(alias);
|
||||||
@ -1707,7 +1724,6 @@ _copyInsertStmt(InsertStmt *from)
|
|||||||
|
|
||||||
COPY_NODE_FIELD(relation);
|
COPY_NODE_FIELD(relation);
|
||||||
COPY_NODE_FIELD(cols);
|
COPY_NODE_FIELD(cols);
|
||||||
COPY_NODE_FIELD(targetList);
|
|
||||||
COPY_NODE_FIELD(selectStmt);
|
COPY_NODE_FIELD(selectStmt);
|
||||||
|
|
||||||
return newnode;
|
return newnode;
|
||||||
@ -1754,6 +1770,7 @@ _copySelectStmt(SelectStmt *from)
|
|||||||
COPY_NODE_FIELD(whereClause);
|
COPY_NODE_FIELD(whereClause);
|
||||||
COPY_NODE_FIELD(groupClause);
|
COPY_NODE_FIELD(groupClause);
|
||||||
COPY_NODE_FIELD(havingClause);
|
COPY_NODE_FIELD(havingClause);
|
||||||
|
COPY_NODE_FIELD(valuesLists);
|
||||||
COPY_NODE_FIELD(sortClause);
|
COPY_NODE_FIELD(sortClause);
|
||||||
COPY_NODE_FIELD(limitOffset);
|
COPY_NODE_FIELD(limitOffset);
|
||||||
COPY_NODE_FIELD(limitCount);
|
COPY_NODE_FIELD(limitCount);
|
||||||
@ -2812,6 +2829,9 @@ copyObject(void *from)
|
|||||||
case T_FunctionScan:
|
case T_FunctionScan:
|
||||||
retval = _copyFunctionScan(from);
|
retval = _copyFunctionScan(from);
|
||||||
break;
|
break;
|
||||||
|
case T_ValuesScan:
|
||||||
|
retval = _copyValuesScan(from);
|
||||||
|
break;
|
||||||
case T_Join:
|
case T_Join:
|
||||||
retval = _copyJoin(from);
|
retval = _copyJoin(from);
|
||||||
break;
|
break;
|
||||||
|
@ -18,7 +18,7 @@
|
|||||||
* Portions Copyright (c) 1994, Regents of the University of California
|
* Portions Copyright (c) 1994, Regents of the University of California
|
||||||
*
|
*
|
||||||
* IDENTIFICATION
|
* IDENTIFICATION
|
||||||
* $PostgreSQL: pgsql/src/backend/nodes/equalfuncs.c,v 1.278 2006/07/27 19:52:05 tgl Exp $
|
* $PostgreSQL: pgsql/src/backend/nodes/equalfuncs.c,v 1.279 2006/08/02 01:59:45 joe Exp $
|
||||||
*
|
*
|
||||||
*-------------------------------------------------------------------------
|
*-------------------------------------------------------------------------
|
||||||
*/
|
*/
|
||||||
@ -682,7 +682,6 @@ _equalInsertStmt(InsertStmt *a, InsertStmt *b)
|
|||||||
{
|
{
|
||||||
COMPARE_NODE_FIELD(relation);
|
COMPARE_NODE_FIELD(relation);
|
||||||
COMPARE_NODE_FIELD(cols);
|
COMPARE_NODE_FIELD(cols);
|
||||||
COMPARE_NODE_FIELD(targetList);
|
|
||||||
COMPARE_NODE_FIELD(selectStmt);
|
COMPARE_NODE_FIELD(selectStmt);
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
@ -723,6 +722,7 @@ _equalSelectStmt(SelectStmt *a, SelectStmt *b)
|
|||||||
COMPARE_NODE_FIELD(whereClause);
|
COMPARE_NODE_FIELD(whereClause);
|
||||||
COMPARE_NODE_FIELD(groupClause);
|
COMPARE_NODE_FIELD(groupClause);
|
||||||
COMPARE_NODE_FIELD(havingClause);
|
COMPARE_NODE_FIELD(havingClause);
|
||||||
|
COMPARE_NODE_FIELD(valuesLists);
|
||||||
COMPARE_NODE_FIELD(sortClause);
|
COMPARE_NODE_FIELD(sortClause);
|
||||||
COMPARE_NODE_FIELD(limitOffset);
|
COMPARE_NODE_FIELD(limitOffset);
|
||||||
COMPARE_NODE_FIELD(limitCount);
|
COMPARE_NODE_FIELD(limitCount);
|
||||||
@ -1706,6 +1706,7 @@ _equalRangeTblEntry(RangeTblEntry *a, RangeTblEntry *b)
|
|||||||
COMPARE_NODE_FIELD(funcexpr);
|
COMPARE_NODE_FIELD(funcexpr);
|
||||||
COMPARE_NODE_FIELD(funccoltypes);
|
COMPARE_NODE_FIELD(funccoltypes);
|
||||||
COMPARE_NODE_FIELD(funccoltypmods);
|
COMPARE_NODE_FIELD(funccoltypmods);
|
||||||
|
COMPARE_NODE_FIELD(values_lists);
|
||||||
COMPARE_SCALAR_FIELD(jointype);
|
COMPARE_SCALAR_FIELD(jointype);
|
||||||
COMPARE_NODE_FIELD(joinaliasvars);
|
COMPARE_NODE_FIELD(joinaliasvars);
|
||||||
COMPARE_NODE_FIELD(alias);
|
COMPARE_NODE_FIELD(alias);
|
||||||
|
@ -8,7 +8,7 @@
|
|||||||
*
|
*
|
||||||
*
|
*
|
||||||
* IDENTIFICATION
|
* IDENTIFICATION
|
||||||
* $PostgreSQL: pgsql/src/backend/nodes/outfuncs.c,v 1.279 2006/07/27 19:52:05 tgl Exp $
|
* $PostgreSQL: pgsql/src/backend/nodes/outfuncs.c,v 1.280 2006/08/02 01:59:45 joe Exp $
|
||||||
*
|
*
|
||||||
* NOTES
|
* NOTES
|
||||||
* Every node type that can appear in stored rules' parsetrees *must*
|
* Every node type that can appear in stored rules' parsetrees *must*
|
||||||
@ -410,6 +410,14 @@ _outFunctionScan(StringInfo str, FunctionScan *node)
|
|||||||
_outScanInfo(str, (Scan *) node);
|
_outScanInfo(str, (Scan *) node);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static void
|
||||||
|
_outValuesScan(StringInfo str, ValuesScan *node)
|
||||||
|
{
|
||||||
|
WRITE_NODE_TYPE("VALUESSCAN");
|
||||||
|
|
||||||
|
_outScanInfo(str, (Scan *) node);
|
||||||
|
}
|
||||||
|
|
||||||
static void
|
static void
|
||||||
_outJoin(StringInfo str, Join *node)
|
_outJoin(StringInfo str, Join *node)
|
||||||
{
|
{
|
||||||
@ -1381,6 +1389,7 @@ _outSelectStmt(StringInfo str, SelectStmt *node)
|
|||||||
WRITE_NODE_FIELD(whereClause);
|
WRITE_NODE_FIELD(whereClause);
|
||||||
WRITE_NODE_FIELD(groupClause);
|
WRITE_NODE_FIELD(groupClause);
|
||||||
WRITE_NODE_FIELD(havingClause);
|
WRITE_NODE_FIELD(havingClause);
|
||||||
|
WRITE_NODE_FIELD(valuesLists);
|
||||||
WRITE_NODE_FIELD(sortClause);
|
WRITE_NODE_FIELD(sortClause);
|
||||||
WRITE_NODE_FIELD(limitOffset);
|
WRITE_NODE_FIELD(limitOffset);
|
||||||
WRITE_NODE_FIELD(limitCount);
|
WRITE_NODE_FIELD(limitCount);
|
||||||
@ -1591,6 +1600,9 @@ _outRangeTblEntry(StringInfo str, RangeTblEntry *node)
|
|||||||
WRITE_NODE_FIELD(funccoltypes);
|
WRITE_NODE_FIELD(funccoltypes);
|
||||||
WRITE_NODE_FIELD(funccoltypmods);
|
WRITE_NODE_FIELD(funccoltypmods);
|
||||||
break;
|
break;
|
||||||
|
case RTE_VALUES:
|
||||||
|
WRITE_NODE_FIELD(values_lists);
|
||||||
|
break;
|
||||||
case RTE_JOIN:
|
case RTE_JOIN:
|
||||||
WRITE_ENUM_FIELD(jointype, JoinType);
|
WRITE_ENUM_FIELD(jointype, JoinType);
|
||||||
WRITE_NODE_FIELD(joinaliasvars);
|
WRITE_NODE_FIELD(joinaliasvars);
|
||||||
@ -1876,6 +1888,9 @@ _outNode(StringInfo str, void *obj)
|
|||||||
case T_FunctionScan:
|
case T_FunctionScan:
|
||||||
_outFunctionScan(str, obj);
|
_outFunctionScan(str, obj);
|
||||||
break;
|
break;
|
||||||
|
case T_ValuesScan:
|
||||||
|
_outValuesScan(str, obj);
|
||||||
|
break;
|
||||||
case T_Join:
|
case T_Join:
|
||||||
_outJoin(str, obj);
|
_outJoin(str, obj);
|
||||||
break;
|
break;
|
||||||
|
@ -8,7 +8,7 @@
|
|||||||
*
|
*
|
||||||
*
|
*
|
||||||
* IDENTIFICATION
|
* IDENTIFICATION
|
||||||
* $PostgreSQL: pgsql/src/backend/nodes/print.c,v 1.80 2006/07/14 14:52:20 momjian Exp $
|
* $PostgreSQL: pgsql/src/backend/nodes/print.c,v 1.81 2006/08/02 01:59:45 joe Exp $
|
||||||
*
|
*
|
||||||
* HISTORY
|
* HISTORY
|
||||||
* AUTHOR DATE MAJOR EVENT
|
* AUTHOR DATE MAJOR EVENT
|
||||||
@ -275,6 +275,10 @@ print_rt(List *rtable)
|
|||||||
printf("%d\t%s\t[rangefunction]",
|
printf("%d\t%s\t[rangefunction]",
|
||||||
i, rte->eref->aliasname);
|
i, rte->eref->aliasname);
|
||||||
break;
|
break;
|
||||||
|
case RTE_VALUES:
|
||||||
|
printf("%d\t%s\t[values list]",
|
||||||
|
i, rte->eref->aliasname);
|
||||||
|
break;
|
||||||
case RTE_JOIN:
|
case RTE_JOIN:
|
||||||
printf("%d\t%s\t[join]",
|
printf("%d\t%s\t[join]",
|
||||||
i, rte->eref->aliasname);
|
i, rte->eref->aliasname);
|
||||||
@ -507,6 +511,8 @@ plannode_type(Plan *p)
|
|||||||
return "SUBQUERYSCAN";
|
return "SUBQUERYSCAN";
|
||||||
case T_FunctionScan:
|
case T_FunctionScan:
|
||||||
return "FUNCTIONSCAN";
|
return "FUNCTIONSCAN";
|
||||||
|
case T_ValuesScan:
|
||||||
|
return "VALUESSCAN";
|
||||||
case T_Join:
|
case T_Join:
|
||||||
return "JOIN";
|
return "JOIN";
|
||||||
case T_NestLoop:
|
case T_NestLoop:
|
||||||
@ -575,6 +581,13 @@ print_plan_recursive(Plan *p, Query *parsetree, int indentLevel, char *label)
|
|||||||
rte = rt_fetch(((FunctionScan *) p)->scan.scanrelid, parsetree->rtable);
|
rte = rt_fetch(((FunctionScan *) p)->scan.scanrelid, parsetree->rtable);
|
||||||
StrNCpy(extraInfo, rte->eref->aliasname, NAMEDATALEN);
|
StrNCpy(extraInfo, rte->eref->aliasname, NAMEDATALEN);
|
||||||
}
|
}
|
||||||
|
else if (IsA(p, ValuesScan))
|
||||||
|
{
|
||||||
|
RangeTblEntry *rte;
|
||||||
|
|
||||||
|
rte = rt_fetch(((ValuesScan *) p)->scan.scanrelid, parsetree->rtable);
|
||||||
|
StrNCpy(extraInfo, rte->eref->aliasname, NAMEDATALEN);
|
||||||
|
}
|
||||||
else
|
else
|
||||||
extraInfo[0] = '\0';
|
extraInfo[0] = '\0';
|
||||||
if (extraInfo[0] != '\0')
|
if (extraInfo[0] != '\0')
|
||||||
|
@ -8,7 +8,7 @@
|
|||||||
*
|
*
|
||||||
*
|
*
|
||||||
* IDENTIFICATION
|
* IDENTIFICATION
|
||||||
* $PostgreSQL: pgsql/src/backend/nodes/readfuncs.c,v 1.192 2006/07/27 19:52:05 tgl Exp $
|
* $PostgreSQL: pgsql/src/backend/nodes/readfuncs.c,v 1.193 2006/08/02 01:59:45 joe Exp $
|
||||||
*
|
*
|
||||||
* NOTES
|
* NOTES
|
||||||
* Path and Plan nodes do not have any readfuncs support, because we
|
* Path and Plan nodes do not have any readfuncs support, because we
|
||||||
@ -902,6 +902,9 @@ _readRangeTblEntry(void)
|
|||||||
READ_NODE_FIELD(funccoltypes);
|
READ_NODE_FIELD(funccoltypes);
|
||||||
READ_NODE_FIELD(funccoltypmods);
|
READ_NODE_FIELD(funccoltypmods);
|
||||||
break;
|
break;
|
||||||
|
case RTE_VALUES:
|
||||||
|
READ_NODE_FIELD(values_lists);
|
||||||
|
break;
|
||||||
case RTE_JOIN:
|
case RTE_JOIN:
|
||||||
READ_ENUM_FIELD(jointype, JoinType);
|
READ_ENUM_FIELD(jointype, JoinType);
|
||||||
READ_NODE_FIELD(joinaliasvars);
|
READ_NODE_FIELD(joinaliasvars);
|
||||||
|
@ -8,7 +8,7 @@
|
|||||||
*
|
*
|
||||||
*
|
*
|
||||||
* IDENTIFICATION
|
* IDENTIFICATION
|
||||||
* $PostgreSQL: pgsql/src/backend/optimizer/path/allpaths.c,v 1.149 2006/07/14 14:52:20 momjian Exp $
|
* $PostgreSQL: pgsql/src/backend/optimizer/path/allpaths.c,v 1.150 2006/08/02 01:59:45 joe Exp $
|
||||||
*
|
*
|
||||||
*-------------------------------------------------------------------------
|
*-------------------------------------------------------------------------
|
||||||
*/
|
*/
|
||||||
@ -48,6 +48,8 @@ static void set_subquery_pathlist(PlannerInfo *root, RelOptInfo *rel,
|
|||||||
Index rti, RangeTblEntry *rte);
|
Index rti, RangeTblEntry *rte);
|
||||||
static void set_function_pathlist(PlannerInfo *root, RelOptInfo *rel,
|
static void set_function_pathlist(PlannerInfo *root, RelOptInfo *rel,
|
||||||
RangeTblEntry *rte);
|
RangeTblEntry *rte);
|
||||||
|
static void set_values_pathlist(PlannerInfo *root, RelOptInfo *rel,
|
||||||
|
RangeTblEntry *rte);
|
||||||
static RelOptInfo *make_rel_from_joinlist(PlannerInfo *root, List *joinlist);
|
static RelOptInfo *make_rel_from_joinlist(PlannerInfo *root, List *joinlist);
|
||||||
static RelOptInfo *make_one_rel_by_joins(PlannerInfo *root, int levels_needed,
|
static RelOptInfo *make_one_rel_by_joins(PlannerInfo *root, int levels_needed,
|
||||||
List *initial_rels);
|
List *initial_rels);
|
||||||
@ -170,6 +172,11 @@ set_rel_pathlist(PlannerInfo *root, RelOptInfo *rel, Index rti)
|
|||||||
/* RangeFunction --- generate a separate plan for it */
|
/* RangeFunction --- generate a separate plan for it */
|
||||||
set_function_pathlist(root, rel, rte);
|
set_function_pathlist(root, rel, rte);
|
||||||
}
|
}
|
||||||
|
else if (rel->rtekind == RTE_VALUES)
|
||||||
|
{
|
||||||
|
/* Values list --- generate a separate plan for it */
|
||||||
|
set_values_pathlist(root, rel, rte);
|
||||||
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
/* Plain relation */
|
/* Plain relation */
|
||||||
@ -537,6 +544,23 @@ set_function_pathlist(PlannerInfo *root, RelOptInfo *rel, RangeTblEntry *rte)
|
|||||||
set_cheapest(rel);
|
set_cheapest(rel);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* set_values_pathlist
|
||||||
|
* Build the (single) access path for a VALUES RTE
|
||||||
|
*/
|
||||||
|
static void
|
||||||
|
set_values_pathlist(PlannerInfo *root, RelOptInfo *rel, RangeTblEntry *rte)
|
||||||
|
{
|
||||||
|
/* Mark rel with estimated output rows, width, etc */
|
||||||
|
set_values_size_estimates(root, rel);
|
||||||
|
|
||||||
|
/* Generate appropriate path */
|
||||||
|
add_path(rel, create_valuesscan_path(root, rel));
|
||||||
|
|
||||||
|
/* Select cheapest path (pretty easy in this case...) */
|
||||||
|
set_cheapest(rel);
|
||||||
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* make_rel_from_joinlist
|
* make_rel_from_joinlist
|
||||||
* Build access paths using a "joinlist" to guide the join path search.
|
* Build access paths using a "joinlist" to guide the join path search.
|
||||||
|
@ -54,7 +54,7 @@
|
|||||||
* Portions Copyright (c) 1994, Regents of the University of California
|
* Portions Copyright (c) 1994, Regents of the University of California
|
||||||
*
|
*
|
||||||
* IDENTIFICATION
|
* IDENTIFICATION
|
||||||
* $PostgreSQL: pgsql/src/backend/optimizer/path/costsize.c,v 1.164 2006/07/26 11:35:56 petere Exp $
|
* $PostgreSQL: pgsql/src/backend/optimizer/path/costsize.c,v 1.165 2006/08/02 01:59:45 joe Exp $
|
||||||
*
|
*
|
||||||
*-------------------------------------------------------------------------
|
*-------------------------------------------------------------------------
|
||||||
*/
|
*/
|
||||||
@ -774,6 +774,36 @@ cost_functionscan(Path *path, PlannerInfo *root, RelOptInfo *baserel)
|
|||||||
path->total_cost = startup_cost + run_cost;
|
path->total_cost = startup_cost + run_cost;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* cost_valuesscan
|
||||||
|
* Determines and returns the cost of scanning a VALUES RTE.
|
||||||
|
*/
|
||||||
|
void
|
||||||
|
cost_valuesscan(Path *path, PlannerInfo *root, RelOptInfo *baserel)
|
||||||
|
{
|
||||||
|
Cost startup_cost = 0;
|
||||||
|
Cost run_cost = 0;
|
||||||
|
Cost cpu_per_tuple;
|
||||||
|
|
||||||
|
/* Should only be applied to base relations that are values lists */
|
||||||
|
Assert(baserel->relid > 0);
|
||||||
|
Assert(baserel->rtekind == RTE_VALUES);
|
||||||
|
|
||||||
|
/*
|
||||||
|
* For now, estimate list evaluation cost at one operator eval per
|
||||||
|
* list (probably pretty bogus, but is it worth being smarter?)
|
||||||
|
*/
|
||||||
|
cpu_per_tuple = cpu_operator_cost;
|
||||||
|
|
||||||
|
/* Add scanning CPU costs */
|
||||||
|
startup_cost += baserel->baserestrictcost.startup;
|
||||||
|
cpu_per_tuple += cpu_tuple_cost + baserel->baserestrictcost.per_tuple;
|
||||||
|
run_cost += cpu_per_tuple * baserel->tuples;
|
||||||
|
|
||||||
|
path->startup_cost = startup_cost;
|
||||||
|
path->total_cost = startup_cost + run_cost;
|
||||||
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* cost_sort
|
* cost_sort
|
||||||
* Determines and returns the cost of sorting a relation, including
|
* Determines and returns the cost of sorting a relation, including
|
||||||
@ -2023,6 +2053,37 @@ set_function_size_estimates(PlannerInfo *root, RelOptInfo *rel)
|
|||||||
set_baserel_size_estimates(root, rel);
|
set_baserel_size_estimates(root, rel);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* set_values_size_estimates
|
||||||
|
* Set the size estimates for a base relation that is a values list.
|
||||||
|
*
|
||||||
|
* The rel's targetlist and restrictinfo list must have been constructed
|
||||||
|
* already.
|
||||||
|
*
|
||||||
|
* We set the same fields as set_baserel_size_estimates.
|
||||||
|
*/
|
||||||
|
void
|
||||||
|
set_values_size_estimates(PlannerInfo *root, RelOptInfo *rel)
|
||||||
|
{
|
||||||
|
RangeTblEntry *rte;
|
||||||
|
|
||||||
|
/* Should only be applied to base relations that are values lists */
|
||||||
|
Assert(rel->relid > 0);
|
||||||
|
rte = rt_fetch(rel->relid, root->parse->rtable);
|
||||||
|
Assert(rte->rtekind == RTE_VALUES);
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Estimate number of rows the values list will return.
|
||||||
|
* We know this precisely based on the list length (well,
|
||||||
|
* barring set-returning functions in list items, but that's
|
||||||
|
* a refinement not catered for anywhere else either).
|
||||||
|
*/
|
||||||
|
rel->tuples = list_length(rte->values_lists);
|
||||||
|
|
||||||
|
/* Now estimate number of output rows, etc */
|
||||||
|
set_baserel_size_estimates(root, rel);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* set_rel_width
|
* set_rel_width
|
||||||
|
@ -10,7 +10,7 @@
|
|||||||
*
|
*
|
||||||
*
|
*
|
||||||
* IDENTIFICATION
|
* IDENTIFICATION
|
||||||
* $PostgreSQL: pgsql/src/backend/optimizer/plan/createplan.c,v 1.215 2006/07/26 00:34:48 momjian Exp $
|
* $PostgreSQL: pgsql/src/backend/optimizer/plan/createplan.c,v 1.216 2006/08/02 01:59:45 joe Exp $
|
||||||
*
|
*
|
||||||
*-------------------------------------------------------------------------
|
*-------------------------------------------------------------------------
|
||||||
*/
|
*/
|
||||||
@ -59,6 +59,8 @@ static SubqueryScan *create_subqueryscan_plan(PlannerInfo *root, Path *best_path
|
|||||||
List *tlist, List *scan_clauses);
|
List *tlist, List *scan_clauses);
|
||||||
static FunctionScan *create_functionscan_plan(PlannerInfo *root, Path *best_path,
|
static FunctionScan *create_functionscan_plan(PlannerInfo *root, Path *best_path,
|
||||||
List *tlist, List *scan_clauses);
|
List *tlist, List *scan_clauses);
|
||||||
|
static ValuesScan *create_valuesscan_plan(PlannerInfo *root, Path *best_path,
|
||||||
|
List *tlist, List *scan_clauses);
|
||||||
static NestLoop *create_nestloop_plan(PlannerInfo *root, NestPath *best_path,
|
static NestLoop *create_nestloop_plan(PlannerInfo *root, NestPath *best_path,
|
||||||
Plan *outer_plan, Plan *inner_plan);
|
Plan *outer_plan, Plan *inner_plan);
|
||||||
static MergeJoin *create_mergejoin_plan(PlannerInfo *root, MergePath *best_path,
|
static MergeJoin *create_mergejoin_plan(PlannerInfo *root, MergePath *best_path,
|
||||||
@ -95,6 +97,8 @@ static TidScan *make_tidscan(List *qptlist, List *qpqual, Index scanrelid,
|
|||||||
List *tidquals);
|
List *tidquals);
|
||||||
static FunctionScan *make_functionscan(List *qptlist, List *qpqual,
|
static FunctionScan *make_functionscan(List *qptlist, List *qpqual,
|
||||||
Index scanrelid);
|
Index scanrelid);
|
||||||
|
static ValuesScan *make_valuesscan(List *qptlist, List *qpqual,
|
||||||
|
Index scanrelid);
|
||||||
static BitmapAnd *make_bitmap_and(List *bitmapplans);
|
static BitmapAnd *make_bitmap_and(List *bitmapplans);
|
||||||
static BitmapOr *make_bitmap_or(List *bitmapplans);
|
static BitmapOr *make_bitmap_or(List *bitmapplans);
|
||||||
static NestLoop *make_nestloop(List *tlist,
|
static NestLoop *make_nestloop(List *tlist,
|
||||||
@ -146,6 +150,7 @@ create_plan(PlannerInfo *root, Path *best_path)
|
|||||||
case T_TidScan:
|
case T_TidScan:
|
||||||
case T_SubqueryScan:
|
case T_SubqueryScan:
|
||||||
case T_FunctionScan:
|
case T_FunctionScan:
|
||||||
|
case T_ValuesScan:
|
||||||
plan = create_scan_plan(root, best_path);
|
plan = create_scan_plan(root, best_path);
|
||||||
break;
|
break;
|
||||||
case T_HashJoin:
|
case T_HashJoin:
|
||||||
@ -262,6 +267,13 @@ create_scan_plan(PlannerInfo *root, Path *best_path)
|
|||||||
scan_clauses);
|
scan_clauses);
|
||||||
break;
|
break;
|
||||||
|
|
||||||
|
case T_ValuesScan:
|
||||||
|
plan = (Plan *) create_valuesscan_plan(root,
|
||||||
|
best_path,
|
||||||
|
tlist,
|
||||||
|
scan_clauses);
|
||||||
|
break;
|
||||||
|
|
||||||
default:
|
default:
|
||||||
elog(ERROR, "unrecognized node type: %d",
|
elog(ERROR, "unrecognized node type: %d",
|
||||||
(int) best_path->pathtype);
|
(int) best_path->pathtype);
|
||||||
@ -315,12 +327,13 @@ use_physical_tlist(RelOptInfo *rel)
|
|||||||
int i;
|
int i;
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* We can do this for real relation scans, subquery scans, and function
|
* We can do this for real relation scans, subquery scans, function
|
||||||
* scans (but not for, eg, joins).
|
* scans, and values scans (but not for, eg, joins).
|
||||||
*/
|
*/
|
||||||
if (rel->rtekind != RTE_RELATION &&
|
if (rel->rtekind != RTE_RELATION &&
|
||||||
rel->rtekind != RTE_SUBQUERY &&
|
rel->rtekind != RTE_SUBQUERY &&
|
||||||
rel->rtekind != RTE_FUNCTION)
|
rel->rtekind != RTE_FUNCTION &&
|
||||||
|
rel->rtekind != RTE_VALUES)
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
/*
|
/*
|
||||||
@ -365,6 +378,7 @@ disuse_physical_tlist(Plan *plan, Path *path)
|
|||||||
case T_TidScan:
|
case T_TidScan:
|
||||||
case T_SubqueryScan:
|
case T_SubqueryScan:
|
||||||
case T_FunctionScan:
|
case T_FunctionScan:
|
||||||
|
case T_ValuesScan:
|
||||||
plan->targetlist = build_relation_tlist(path->parent);
|
plan->targetlist = build_relation_tlist(path->parent);
|
||||||
break;
|
break;
|
||||||
default:
|
default:
|
||||||
@ -1312,6 +1326,35 @@ create_functionscan_plan(PlannerInfo *root, Path *best_path,
|
|||||||
return scan_plan;
|
return scan_plan;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* create_valuesscan_plan
|
||||||
|
* Returns a valuesscan plan for the base relation scanned by 'best_path'
|
||||||
|
* with restriction clauses 'scan_clauses' and targetlist 'tlist'.
|
||||||
|
*/
|
||||||
|
static ValuesScan *
|
||||||
|
create_valuesscan_plan(PlannerInfo *root, Path *best_path,
|
||||||
|
List *tlist, List *scan_clauses)
|
||||||
|
{
|
||||||
|
ValuesScan *scan_plan;
|
||||||
|
Index scan_relid = best_path->parent->relid;
|
||||||
|
|
||||||
|
/* it should be a values base rel... */
|
||||||
|
Assert(scan_relid > 0);
|
||||||
|
Assert(best_path->parent->rtekind == RTE_VALUES);
|
||||||
|
|
||||||
|
/* Reduce RestrictInfo list to bare expressions; ignore pseudoconstants */
|
||||||
|
scan_clauses = extract_actual_clauses(scan_clauses, false);
|
||||||
|
|
||||||
|
/* Sort clauses into best execution order */
|
||||||
|
scan_clauses = order_qual_clauses(root, scan_clauses);
|
||||||
|
|
||||||
|
scan_plan = make_valuesscan(tlist, scan_clauses, scan_relid);
|
||||||
|
|
||||||
|
copy_path_costsize(&scan_plan->scan.plan, best_path);
|
||||||
|
|
||||||
|
return scan_plan;
|
||||||
|
}
|
||||||
|
|
||||||
/*****************************************************************************
|
/*****************************************************************************
|
||||||
*
|
*
|
||||||
* JOIN METHODS
|
* JOIN METHODS
|
||||||
@ -2123,6 +2166,24 @@ make_functionscan(List *qptlist,
|
|||||||
return node;
|
return node;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static ValuesScan *
|
||||||
|
make_valuesscan(List *qptlist,
|
||||||
|
List *qpqual,
|
||||||
|
Index scanrelid)
|
||||||
|
{
|
||||||
|
ValuesScan *node = makeNode(ValuesScan);
|
||||||
|
Plan *plan = &node->scan.plan;
|
||||||
|
|
||||||
|
/* cost should be inserted by caller */
|
||||||
|
plan->targetlist = qptlist;
|
||||||
|
plan->qual = qpqual;
|
||||||
|
plan->lefttree = NULL;
|
||||||
|
plan->righttree = NULL;
|
||||||
|
node->scan.scanrelid = scanrelid;
|
||||||
|
|
||||||
|
return node;
|
||||||
|
}
|
||||||
|
|
||||||
Append *
|
Append *
|
||||||
make_append(List *appendplans, bool isTarget, List *tlist)
|
make_append(List *appendplans, bool isTarget, List *tlist)
|
||||||
{
|
{
|
||||||
|
@ -8,7 +8,7 @@
|
|||||||
*
|
*
|
||||||
*
|
*
|
||||||
* IDENTIFICATION
|
* IDENTIFICATION
|
||||||
* $PostgreSQL: pgsql/src/backend/optimizer/plan/planner.c,v 1.205 2006/07/26 19:31:50 tgl Exp $
|
* $PostgreSQL: pgsql/src/backend/optimizer/plan/planner.c,v 1.206 2006/08/02 01:59:46 joe Exp $
|
||||||
*
|
*
|
||||||
*-------------------------------------------------------------------------
|
*-------------------------------------------------------------------------
|
||||||
*/
|
*/
|
||||||
@ -48,9 +48,10 @@ ParamListInfo PlannerBoundParamList = NULL; /* current boundParams */
|
|||||||
#define EXPRKIND_QUAL 0
|
#define EXPRKIND_QUAL 0
|
||||||
#define EXPRKIND_TARGET 1
|
#define EXPRKIND_TARGET 1
|
||||||
#define EXPRKIND_RTFUNC 2
|
#define EXPRKIND_RTFUNC 2
|
||||||
#define EXPRKIND_LIMIT 3
|
#define EXPRKIND_VALUES 3
|
||||||
#define EXPRKIND_ININFO 4
|
#define EXPRKIND_LIMIT 4
|
||||||
#define EXPRKIND_APPINFO 5
|
#define EXPRKIND_ININFO 5
|
||||||
|
#define EXPRKIND_APPINFO 6
|
||||||
|
|
||||||
|
|
||||||
static Node *preprocess_expression(PlannerInfo *root, Node *expr, int kind);
|
static Node *preprocess_expression(PlannerInfo *root, Node *expr, int kind);
|
||||||
@ -295,7 +296,7 @@ subquery_planner(Query *parse, double tuple_fraction,
|
|||||||
preprocess_expression(root, (Node *) root->append_rel_list,
|
preprocess_expression(root, (Node *) root->append_rel_list,
|
||||||
EXPRKIND_APPINFO);
|
EXPRKIND_APPINFO);
|
||||||
|
|
||||||
/* Also need to preprocess expressions for function RTEs */
|
/* Also need to preprocess expressions for function and values RTEs */
|
||||||
foreach(l, parse->rtable)
|
foreach(l, parse->rtable)
|
||||||
{
|
{
|
||||||
RangeTblEntry *rte = (RangeTblEntry *) lfirst(l);
|
RangeTblEntry *rte = (RangeTblEntry *) lfirst(l);
|
||||||
@ -303,6 +304,10 @@ subquery_planner(Query *parse, double tuple_fraction,
|
|||||||
if (rte->rtekind == RTE_FUNCTION)
|
if (rte->rtekind == RTE_FUNCTION)
|
||||||
rte->funcexpr = preprocess_expression(root, rte->funcexpr,
|
rte->funcexpr = preprocess_expression(root, rte->funcexpr,
|
||||||
EXPRKIND_RTFUNC);
|
EXPRKIND_RTFUNC);
|
||||||
|
else if (rte->rtekind == RTE_VALUES)
|
||||||
|
rte->values_lists = (List *)
|
||||||
|
preprocess_expression(root, (Node *) rte->values_lists,
|
||||||
|
EXPRKIND_VALUES);
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
@ -418,8 +423,10 @@ preprocess_expression(PlannerInfo *root, Node *expr, int kind)
|
|||||||
* If the query has any join RTEs, replace join alias variables with
|
* If the query has any join RTEs, replace join alias variables with
|
||||||
* base-relation variables. We must do this before sublink processing,
|
* base-relation variables. We must do this before sublink processing,
|
||||||
* else sublinks expanded out from join aliases wouldn't get processed.
|
* else sublinks expanded out from join aliases wouldn't get processed.
|
||||||
|
* We can skip it in VALUES lists, however, since they can't contain
|
||||||
|
* any Vars at all.
|
||||||
*/
|
*/
|
||||||
if (root->hasJoinRTEs)
|
if (root->hasJoinRTEs && kind != EXPRKIND_VALUES)
|
||||||
expr = flatten_join_alias_vars(root, expr);
|
expr = flatten_join_alias_vars(root, expr);
|
||||||
|
|
||||||
/*
|
/*
|
||||||
@ -437,10 +444,14 @@ preprocess_expression(PlannerInfo *root, Node *expr, int kind)
|
|||||||
* and we will waste cycles copying the tree. Notice however that we
|
* and we will waste cycles copying the tree. Notice however that we
|
||||||
* still must do it for quals (to get AND/OR flatness); and if we are in a
|
* still must do it for quals (to get AND/OR flatness); and if we are in a
|
||||||
* subquery we should not assume it will be done only once.
|
* subquery we should not assume it will be done only once.
|
||||||
|
*
|
||||||
|
* For VALUES lists we never do this at all, again on the grounds that
|
||||||
|
* we should optimize for one-time evaluation.
|
||||||
*/
|
*/
|
||||||
if (root->parse->jointree->fromlist != NIL ||
|
if (kind != EXPRKIND_VALUES &&
|
||||||
kind == EXPRKIND_QUAL ||
|
(root->parse->jointree->fromlist != NIL ||
|
||||||
PlannerQueryLevel > 1)
|
kind == EXPRKIND_QUAL ||
|
||||||
|
PlannerQueryLevel > 1))
|
||||||
expr = eval_const_expressions(expr);
|
expr = eval_const_expressions(expr);
|
||||||
|
|
||||||
/*
|
/*
|
||||||
@ -465,7 +476,7 @@ preprocess_expression(PlannerInfo *root, Node *expr, int kind)
|
|||||||
* SS_replace_correlation_vars ...
|
* SS_replace_correlation_vars ...
|
||||||
*/
|
*/
|
||||||
|
|
||||||
/* Replace uplevel vars with Param nodes */
|
/* Replace uplevel vars with Param nodes (this IS possible in VALUES) */
|
||||||
if (PlannerQueryLevel > 1)
|
if (PlannerQueryLevel > 1)
|
||||||
expr = SS_replace_correlation_vars(expr);
|
expr = SS_replace_correlation_vars(expr);
|
||||||
|
|
||||||
|
@ -9,7 +9,7 @@
|
|||||||
*
|
*
|
||||||
*
|
*
|
||||||
* IDENTIFICATION
|
* IDENTIFICATION
|
||||||
* $PostgreSQL: pgsql/src/backend/optimizer/plan/setrefs.c,v 1.122 2006/07/14 14:52:21 momjian Exp $
|
* $PostgreSQL: pgsql/src/backend/optimizer/plan/setrefs.c,v 1.123 2006/08/02 01:59:46 joe Exp $
|
||||||
*
|
*
|
||||||
*-------------------------------------------------------------------------
|
*-------------------------------------------------------------------------
|
||||||
*/
|
*/
|
||||||
@ -186,6 +186,18 @@ set_plan_references(Plan *plan, List *rtable)
|
|||||||
fix_expr_references(plan, rte->funcexpr);
|
fix_expr_references(plan, rte->funcexpr);
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
|
case T_ValuesScan:
|
||||||
|
{
|
||||||
|
RangeTblEntry *rte;
|
||||||
|
|
||||||
|
fix_expr_references(plan, (Node *) plan->targetlist);
|
||||||
|
fix_expr_references(plan, (Node *) plan->qual);
|
||||||
|
rte = rt_fetch(((ValuesScan *) plan)->scan.scanrelid,
|
||||||
|
rtable);
|
||||||
|
Assert(rte->rtekind == RTE_VALUES);
|
||||||
|
fix_expr_references(plan, (Node *) rte->values_lists);
|
||||||
|
}
|
||||||
|
break;
|
||||||
case T_NestLoop:
|
case T_NestLoop:
|
||||||
set_join_references((Join *) plan, rtable);
|
set_join_references((Join *) plan, rtable);
|
||||||
fix_expr_references(plan, (Node *) plan->targetlist);
|
fix_expr_references(plan, (Node *) plan->targetlist);
|
||||||
@ -522,6 +534,12 @@ adjust_plan_varnos(Plan *plan, int rtoffset)
|
|||||||
adjust_expr_varnos((Node *) plan->qual, rtoffset);
|
adjust_expr_varnos((Node *) plan->qual, rtoffset);
|
||||||
/* rte was already fixed by set_subqueryscan_references */
|
/* rte was already fixed by set_subqueryscan_references */
|
||||||
break;
|
break;
|
||||||
|
case T_ValuesScan:
|
||||||
|
((ValuesScan *) plan)->scan.scanrelid += rtoffset;
|
||||||
|
adjust_expr_varnos((Node *) plan->targetlist, rtoffset);
|
||||||
|
adjust_expr_varnos((Node *) plan->qual, rtoffset);
|
||||||
|
/* rte was already fixed by set_subqueryscan_references */
|
||||||
|
break;
|
||||||
case T_NestLoop:
|
case T_NestLoop:
|
||||||
adjust_expr_varnos((Node *) plan->targetlist, rtoffset);
|
adjust_expr_varnos((Node *) plan->targetlist, rtoffset);
|
||||||
adjust_expr_varnos((Node *) plan->qual, rtoffset);
|
adjust_expr_varnos((Node *) plan->qual, rtoffset);
|
||||||
|
@ -7,7 +7,7 @@
|
|||||||
* Portions Copyright (c) 1994, Regents of the University of California
|
* Portions Copyright (c) 1994, Regents of the University of California
|
||||||
*
|
*
|
||||||
* IDENTIFICATION
|
* IDENTIFICATION
|
||||||
* $PostgreSQL: pgsql/src/backend/optimizer/plan/subselect.c,v 1.110 2006/07/14 14:52:21 momjian Exp $
|
* $PostgreSQL: pgsql/src/backend/optimizer/plan/subselect.c,v 1.111 2006/08/02 01:59:46 joe Exp $
|
||||||
*
|
*
|
||||||
*-------------------------------------------------------------------------
|
*-------------------------------------------------------------------------
|
||||||
*/
|
*/
|
||||||
@ -1090,6 +1090,17 @@ finalize_plan(Plan *plan, List *rtable,
|
|||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
|
|
||||||
|
case T_ValuesScan:
|
||||||
|
{
|
||||||
|
RangeTblEntry *rte;
|
||||||
|
|
||||||
|
rte = rt_fetch(((ValuesScan *) plan)->scan.scanrelid,
|
||||||
|
rtable);
|
||||||
|
Assert(rte->rtekind == RTE_VALUES);
|
||||||
|
finalize_primnode((Node *) rte->values_lists, &context);
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
|
||||||
case T_Append:
|
case T_Append:
|
||||||
{
|
{
|
||||||
ListCell *l;
|
ListCell *l;
|
||||||
|
@ -8,7 +8,7 @@
|
|||||||
*
|
*
|
||||||
*
|
*
|
||||||
* IDENTIFICATION
|
* IDENTIFICATION
|
||||||
* $PostgreSQL: pgsql/src/backend/optimizer/util/clauses.c,v 1.215 2006/07/27 19:52:05 tgl Exp $
|
* $PostgreSQL: pgsql/src/backend/optimizer/util/clauses.c,v 1.216 2006/08/02 01:59:46 joe Exp $
|
||||||
*
|
*
|
||||||
* HISTORY
|
* HISTORY
|
||||||
* AUTHOR DATE MAJOR EVENT
|
* AUTHOR DATE MAJOR EVENT
|
||||||
@ -3351,6 +3351,10 @@ range_table_walker(List *rtable,
|
|||||||
if (walker(rte->funcexpr, context))
|
if (walker(rte->funcexpr, context))
|
||||||
return true;
|
return true;
|
||||||
break;
|
break;
|
||||||
|
case RTE_VALUES:
|
||||||
|
if (walker(rte->values_lists, context))
|
||||||
|
return true;
|
||||||
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return false;
|
return false;
|
||||||
@ -3917,6 +3921,9 @@ range_table_mutator(List *rtable,
|
|||||||
case RTE_FUNCTION:
|
case RTE_FUNCTION:
|
||||||
MUTATE(newrte->funcexpr, rte->funcexpr, Node *);
|
MUTATE(newrte->funcexpr, rte->funcexpr, Node *);
|
||||||
break;
|
break;
|
||||||
|
case RTE_VALUES:
|
||||||
|
MUTATE(newrte->values_lists, rte->values_lists, List *);
|
||||||
|
break;
|
||||||
}
|
}
|
||||||
newrt = lappend(newrt, newrte);
|
newrt = lappend(newrt, newrte);
|
||||||
}
|
}
|
||||||
|
@ -8,7 +8,7 @@
|
|||||||
*
|
*
|
||||||
*
|
*
|
||||||
* IDENTIFICATION
|
* IDENTIFICATION
|
||||||
* $PostgreSQL: pgsql/src/backend/optimizer/util/pathnode.c,v 1.131 2006/07/22 15:41:55 tgl Exp $
|
* $PostgreSQL: pgsql/src/backend/optimizer/util/pathnode.c,v 1.132 2006/08/02 01:59:46 joe Exp $
|
||||||
*
|
*
|
||||||
*-------------------------------------------------------------------------
|
*-------------------------------------------------------------------------
|
||||||
*/
|
*/
|
||||||
@ -1082,6 +1082,25 @@ create_functionscan_path(PlannerInfo *root, RelOptInfo *rel)
|
|||||||
return pathnode;
|
return pathnode;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* create_valuesscan_path
|
||||||
|
* Creates a path corresponding to a scan of a VALUES list,
|
||||||
|
* returning the pathnode.
|
||||||
|
*/
|
||||||
|
Path *
|
||||||
|
create_valuesscan_path(PlannerInfo *root, RelOptInfo *rel)
|
||||||
|
{
|
||||||
|
Path *pathnode = makeNode(Path);
|
||||||
|
|
||||||
|
pathnode->pathtype = T_ValuesScan;
|
||||||
|
pathnode->parent = rel;
|
||||||
|
pathnode->pathkeys = NIL; /* result is always unordered */
|
||||||
|
|
||||||
|
cost_valuesscan(pathnode, root, rel);
|
||||||
|
|
||||||
|
return pathnode;
|
||||||
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* create_nestloop_path
|
* create_nestloop_path
|
||||||
* Creates a pathnode corresponding to a nestloop join between two
|
* Creates a pathnode corresponding to a nestloop join between two
|
||||||
|
@ -9,7 +9,7 @@
|
|||||||
*
|
*
|
||||||
*
|
*
|
||||||
* IDENTIFICATION
|
* IDENTIFICATION
|
||||||
* $PostgreSQL: pgsql/src/backend/optimizer/util/plancat.c,v 1.122 2006/07/31 20:09:04 tgl Exp $
|
* $PostgreSQL: pgsql/src/backend/optimizer/util/plancat.c,v 1.123 2006/08/02 01:59:46 joe Exp $
|
||||||
*
|
*
|
||||||
*-------------------------------------------------------------------------
|
*-------------------------------------------------------------------------
|
||||||
*/
|
*/
|
||||||
@ -493,9 +493,9 @@ relation_excluded_by_constraints(RelOptInfo *rel, RangeTblEntry *rte)
|
|||||||
* For now, we don't apply the physical-tlist optimization when there are
|
* For now, we don't apply the physical-tlist optimization when there are
|
||||||
* dropped cols.
|
* dropped cols.
|
||||||
*
|
*
|
||||||
* We also support building a "physical" tlist for subqueries and functions,
|
* We also support building a "physical" tlist for subqueries, functions,
|
||||||
* since the same optimization can occur in SubqueryScan and FunctionScan
|
* and values lists, since the same optimization can occur in SubqueryScan,
|
||||||
* nodes.
|
* FunctionScan, and ValuesScan nodes.
|
||||||
*/
|
*/
|
||||||
List *
|
List *
|
||||||
build_physical_tlist(PlannerInfo *root, RelOptInfo *rel)
|
build_physical_tlist(PlannerInfo *root, RelOptInfo *rel)
|
||||||
@ -594,6 +594,21 @@ build_physical_tlist(PlannerInfo *root, RelOptInfo *rel)
|
|||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
|
|
||||||
|
case RTE_VALUES:
|
||||||
|
expandRTE(rte, varno, 0, false /* dropped not applicable */ ,
|
||||||
|
NULL, &colvars);
|
||||||
|
foreach(l, colvars)
|
||||||
|
{
|
||||||
|
var = (Var *) lfirst(l);
|
||||||
|
|
||||||
|
tlist = lappend(tlist,
|
||||||
|
makeTargetEntry((Expr *) var,
|
||||||
|
var->varattno,
|
||||||
|
NULL,
|
||||||
|
false));
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
|
||||||
default:
|
default:
|
||||||
/* caller error */
|
/* caller error */
|
||||||
elog(ERROR, "unsupported RTE kind %d in build_physical_tlist",
|
elog(ERROR, "unsupported RTE kind %d in build_physical_tlist",
|
||||||
|
@ -8,7 +8,7 @@
|
|||||||
*
|
*
|
||||||
*
|
*
|
||||||
* IDENTIFICATION
|
* IDENTIFICATION
|
||||||
* $PostgreSQL: pgsql/src/backend/optimizer/util/relnode.c,v 1.80 2006/07/31 20:09:04 tgl Exp $
|
* $PostgreSQL: pgsql/src/backend/optimizer/util/relnode.c,v 1.81 2006/08/02 01:59:46 joe Exp $
|
||||||
*
|
*
|
||||||
*-------------------------------------------------------------------------
|
*-------------------------------------------------------------------------
|
||||||
*/
|
*/
|
||||||
@ -96,8 +96,13 @@ build_simple_rel(PlannerInfo *root, int relid, RelOptKind reloptkind)
|
|||||||
break;
|
break;
|
||||||
case RTE_SUBQUERY:
|
case RTE_SUBQUERY:
|
||||||
case RTE_FUNCTION:
|
case RTE_FUNCTION:
|
||||||
/* Subquery or function --- set up attr range and arrays */
|
case RTE_VALUES:
|
||||||
/* Note: 0 is included in range to support whole-row Vars */
|
/*
|
||||||
|
* Subquery, function, or values list --- set up attr range
|
||||||
|
* and arrays
|
||||||
|
*
|
||||||
|
* Note: 0 is included in range to support whole-row Vars
|
||||||
|
*/
|
||||||
rel->min_attr = 0;
|
rel->min_attr = 0;
|
||||||
rel->max_attr = list_length(rte->eref->colnames);
|
rel->max_attr = list_length(rte->eref->colnames);
|
||||||
rel->attr_needed = (Relids *)
|
rel->attr_needed = (Relids *)
|
||||||
|
@ -6,7 +6,7 @@
|
|||||||
* Portions Copyright (c) 1996-2006, PostgreSQL Global Development Group
|
* Portions Copyright (c) 1996-2006, PostgreSQL Global Development Group
|
||||||
* Portions Copyright (c) 1994, Regents of the University of California
|
* Portions Copyright (c) 1994, Regents of the University of California
|
||||||
*
|
*
|
||||||
* $PostgreSQL: pgsql/src/backend/parser/analyze.c,v 1.340 2006/07/14 14:52:21 momjian Exp $
|
* $PostgreSQL: pgsql/src/backend/parser/analyze.c,v 1.341 2006/08/02 01:59:46 joe Exp $
|
||||||
*
|
*
|
||||||
*-------------------------------------------------------------------------
|
*-------------------------------------------------------------------------
|
||||||
*/
|
*/
|
||||||
@ -98,10 +98,13 @@ static Query *transformViewStmt(ParseState *pstate, ViewStmt *stmt,
|
|||||||
static Query *transformDeleteStmt(ParseState *pstate, DeleteStmt *stmt);
|
static Query *transformDeleteStmt(ParseState *pstate, DeleteStmt *stmt);
|
||||||
static Query *transformInsertStmt(ParseState *pstate, InsertStmt *stmt,
|
static Query *transformInsertStmt(ParseState *pstate, InsertStmt *stmt,
|
||||||
List **extras_before, List **extras_after);
|
List **extras_before, List **extras_after);
|
||||||
|
static List *transformInsertRow(ParseState *pstate, List *exprlist,
|
||||||
|
List *stmtcols, List *icolumns, List *attrnos);
|
||||||
static Query *transformIndexStmt(ParseState *pstate, IndexStmt *stmt);
|
static Query *transformIndexStmt(ParseState *pstate, IndexStmt *stmt);
|
||||||
static Query *transformRuleStmt(ParseState *query, RuleStmt *stmt,
|
static Query *transformRuleStmt(ParseState *query, RuleStmt *stmt,
|
||||||
List **extras_before, List **extras_after);
|
List **extras_before, List **extras_after);
|
||||||
static Query *transformSelectStmt(ParseState *pstate, SelectStmt *stmt);
|
static Query *transformSelectStmt(ParseState *pstate, SelectStmt *stmt);
|
||||||
|
static Query *transformValuesClause(ParseState *pstate, SelectStmt *stmt);
|
||||||
static Query *transformSetOperationStmt(ParseState *pstate, SelectStmt *stmt);
|
static Query *transformSetOperationStmt(ParseState *pstate, SelectStmt *stmt);
|
||||||
static Node *transformSetOperationTree(ParseState *pstate, SelectStmt *stmt);
|
static Node *transformSetOperationTree(ParseState *pstate, SelectStmt *stmt);
|
||||||
static Query *transformUpdateStmt(ParseState *pstate, UpdateStmt *stmt);
|
static Query *transformUpdateStmt(ParseState *pstate, UpdateStmt *stmt);
|
||||||
@ -367,12 +370,16 @@ transformStmt(ParseState *pstate, Node *parseTree,
|
|||||||
break;
|
break;
|
||||||
|
|
||||||
case T_SelectStmt:
|
case T_SelectStmt:
|
||||||
if (((SelectStmt *) parseTree)->op == SETOP_NONE)
|
{
|
||||||
result = transformSelectStmt(pstate,
|
SelectStmt *n = (SelectStmt *) parseTree;
|
||||||
(SelectStmt *) parseTree);
|
|
||||||
else
|
if (n->valuesLists)
|
||||||
result = transformSetOperationStmt(pstate,
|
result = transformValuesClause(pstate, n);
|
||||||
(SelectStmt *) parseTree);
|
else if (n->op == SETOP_NONE)
|
||||||
|
result = transformSelectStmt(pstate, n);
|
||||||
|
else
|
||||||
|
result = transformSetOperationStmt(pstate, n);
|
||||||
|
}
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case T_DeclareCursorStmt:
|
case T_DeclareCursorStmt:
|
||||||
@ -510,19 +517,30 @@ transformInsertStmt(ParseState *pstate, InsertStmt *stmt,
|
|||||||
List **extras_before, List **extras_after)
|
List **extras_before, List **extras_after)
|
||||||
{
|
{
|
||||||
Query *qry = makeNode(Query);
|
Query *qry = makeNode(Query);
|
||||||
Query *selectQuery = NULL;
|
SelectStmt *selectStmt = (SelectStmt *) stmt->selectStmt;
|
||||||
|
List *exprList = NIL;
|
||||||
|
bool isGeneralSelect;
|
||||||
List *sub_rtable;
|
List *sub_rtable;
|
||||||
List *sub_relnamespace;
|
List *sub_relnamespace;
|
||||||
List *sub_varnamespace;
|
List *sub_varnamespace;
|
||||||
List *icolumns;
|
List *icolumns;
|
||||||
List *attrnos;
|
List *attrnos;
|
||||||
|
RangeTblEntry *rte;
|
||||||
|
RangeTblRef *rtr;
|
||||||
ListCell *icols;
|
ListCell *icols;
|
||||||
ListCell *attnos;
|
ListCell *attnos;
|
||||||
ListCell *tl;
|
ListCell *lc;
|
||||||
|
|
||||||
qry->commandType = CMD_INSERT;
|
qry->commandType = CMD_INSERT;
|
||||||
pstate->p_is_insert = true;
|
pstate->p_is_insert = true;
|
||||||
|
|
||||||
|
/*
|
||||||
|
* We have three cases to deal with: DEFAULT VALUES (selectStmt == NULL),
|
||||||
|
* VALUES list, or general SELECT input. We special-case VALUES, both
|
||||||
|
* for efficiency and so we can handle DEFAULT specifications.
|
||||||
|
*/
|
||||||
|
isGeneralSelect = (selectStmt && selectStmt->valuesLists == NIL);
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* If a non-nil rangetable/namespace was passed in, and we are doing
|
* If a non-nil rangetable/namespace was passed in, and we are doing
|
||||||
* INSERT/SELECT, arrange to pass the rangetable/namespace down to the
|
* INSERT/SELECT, arrange to pass the rangetable/namespace down to the
|
||||||
@ -532,7 +550,7 @@ transformInsertStmt(ParseState *pstate, InsertStmt *stmt,
|
|||||||
* The SELECT's joinlist is not affected however. We must do this before
|
* The SELECT's joinlist is not affected however. We must do this before
|
||||||
* adding the target table to the INSERT's rtable.
|
* adding the target table to the INSERT's rtable.
|
||||||
*/
|
*/
|
||||||
if (stmt->selectStmt)
|
if (isGeneralSelect)
|
||||||
{
|
{
|
||||||
sub_rtable = pstate->p_rtable;
|
sub_rtable = pstate->p_rtable;
|
||||||
pstate->p_rtable = NIL;
|
pstate->p_rtable = NIL;
|
||||||
@ -557,10 +575,23 @@ transformInsertStmt(ParseState *pstate, InsertStmt *stmt,
|
|||||||
qry->resultRelation = setTargetTable(pstate, stmt->relation,
|
qry->resultRelation = setTargetTable(pstate, stmt->relation,
|
||||||
false, false, ACL_INSERT);
|
false, false, ACL_INSERT);
|
||||||
|
|
||||||
|
/* Validate stmt->cols list, or build default list if no list given */
|
||||||
|
icolumns = checkInsertTargets(pstate, stmt->cols, &attrnos);
|
||||||
|
Assert(list_length(icolumns) == list_length(attrnos));
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Is it INSERT ... SELECT or INSERT ... VALUES?
|
* Determine which variant of INSERT we have.
|
||||||
*/
|
*/
|
||||||
if (stmt->selectStmt)
|
if (selectStmt == NULL)
|
||||||
|
{
|
||||||
|
/*
|
||||||
|
* We have INSERT ... DEFAULT VALUES. We can handle this case by
|
||||||
|
* emitting an empty targetlist --- all columns will be defaulted
|
||||||
|
* when the planner expands the targetlist.
|
||||||
|
*/
|
||||||
|
exprList = NIL;
|
||||||
|
}
|
||||||
|
else if (isGeneralSelect)
|
||||||
{
|
{
|
||||||
/*
|
/*
|
||||||
* We make the sub-pstate a child of the outer pstate so that it can
|
* We make the sub-pstate a child of the outer pstate so that it can
|
||||||
@ -570,8 +601,7 @@ transformInsertStmt(ParseState *pstate, InsertStmt *stmt,
|
|||||||
* see.
|
* see.
|
||||||
*/
|
*/
|
||||||
ParseState *sub_pstate = make_parsestate(pstate);
|
ParseState *sub_pstate = make_parsestate(pstate);
|
||||||
RangeTblEntry *rte;
|
Query *selectQuery;
|
||||||
RangeTblRef *rtr;
|
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Process the source SELECT.
|
* Process the source SELECT.
|
||||||
@ -617,8 +647,8 @@ transformInsertStmt(ParseState *pstate, InsertStmt *stmt,
|
|||||||
pstate->p_joinlist = lappend(pstate->p_joinlist, rtr);
|
pstate->p_joinlist = lappend(pstate->p_joinlist, rtr);
|
||||||
|
|
||||||
/*----------
|
/*----------
|
||||||
* Generate a targetlist for the INSERT that selects all the
|
* Generate an expression list for the INSERT that selects all the
|
||||||
* non-resjunk columns from the subquery. (We need this to be
|
* non-resjunk columns from the subquery. (INSERT's tlist must be
|
||||||
* separate from the subquery's tlist because we may add columns,
|
* separate from the subquery's tlist because we may add columns,
|
||||||
* insert datatype coercions, etc.)
|
* insert datatype coercions, etc.)
|
||||||
*
|
*
|
||||||
@ -630,10 +660,10 @@ transformInsertStmt(ParseState *pstate, InsertStmt *stmt,
|
|||||||
* INSERT INTO foo SELECT 'bar', ... FROM baz
|
* INSERT INTO foo SELECT 'bar', ... FROM baz
|
||||||
*----------
|
*----------
|
||||||
*/
|
*/
|
||||||
qry->targetList = NIL;
|
exprList = NIL;
|
||||||
foreach(tl, selectQuery->targetList)
|
foreach(lc, selectQuery->targetList)
|
||||||
{
|
{
|
||||||
TargetEntry *tle = (TargetEntry *) lfirst(tl);
|
TargetEntry *tle = (TargetEntry *) lfirst(lc);
|
||||||
Expr *expr;
|
Expr *expr;
|
||||||
|
|
||||||
if (tle->resjunk)
|
if (tle->resjunk)
|
||||||
@ -648,82 +678,220 @@ transformInsertStmt(ParseState *pstate, InsertStmt *stmt,
|
|||||||
exprType((Node *) tle->expr),
|
exprType((Node *) tle->expr),
|
||||||
exprTypmod((Node *) tle->expr),
|
exprTypmod((Node *) tle->expr),
|
||||||
0);
|
0);
|
||||||
tle = makeTargetEntry(expr,
|
exprList = lappend(exprList, expr);
|
||||||
(AttrNumber) pstate->p_next_resno++,
|
|
||||||
tle->resname,
|
|
||||||
false);
|
|
||||||
qry->targetList = lappend(qry->targetList, tle);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* Prepare row for assignment to target table */
|
||||||
|
exprList = transformInsertRow(pstate, exprList,
|
||||||
|
stmt->cols,
|
||||||
|
icolumns, attrnos);
|
||||||
|
}
|
||||||
|
else if (list_length(selectStmt->valuesLists) > 1)
|
||||||
|
{
|
||||||
|
/*
|
||||||
|
* Process INSERT ... VALUES with multiple VALUES sublists.
|
||||||
|
* We generate a VALUES RTE holding the transformed expression
|
||||||
|
* lists, and build up a targetlist containing Vars that reference
|
||||||
|
* the VALUES RTE.
|
||||||
|
*/
|
||||||
|
List *exprsLists = NIL;
|
||||||
|
int sublist_length = -1;
|
||||||
|
|
||||||
|
foreach(lc, selectStmt->valuesLists)
|
||||||
|
{
|
||||||
|
List *sublist = (List *) lfirst(lc);
|
||||||
|
|
||||||
|
/* Do basic expression transformation (same as a ROW() expr) */
|
||||||
|
sublist = transformExpressionList(pstate, sublist);
|
||||||
|
|
||||||
|
/*
|
||||||
|
* All the sublists must be the same length, *after* transformation
|
||||||
|
* (which might expand '*' into multiple items). The VALUES RTE
|
||||||
|
* can't handle anything different.
|
||||||
|
*/
|
||||||
|
if (sublist_length < 0)
|
||||||
|
{
|
||||||
|
/* Remember post-transformation length of first sublist */
|
||||||
|
sublist_length = list_length(sublist);
|
||||||
|
}
|
||||||
|
else if (sublist_length != list_length(sublist))
|
||||||
|
{
|
||||||
|
ereport(ERROR,
|
||||||
|
(errcode(ERRCODE_SYNTAX_ERROR),
|
||||||
|
errmsg("VALUES lists must all be the same length")));
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Prepare row for assignment to target table */
|
||||||
|
sublist = transformInsertRow(pstate, sublist,
|
||||||
|
stmt->cols,
|
||||||
|
icolumns, attrnos);
|
||||||
|
|
||||||
|
exprsLists = lappend(exprsLists, sublist);
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* There mustn't have been any table references in the expressions,
|
||||||
|
* else strange things would happen, like Cartesian products of
|
||||||
|
* those tables with the VALUES list ...
|
||||||
|
*/
|
||||||
|
if (pstate->p_joinlist != NIL)
|
||||||
|
ereport(ERROR,
|
||||||
|
(errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
|
||||||
|
errmsg("VALUES must not contain table references")));
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Another thing we can't currently support is NEW/OLD references
|
||||||
|
* in rules --- seems we'd need something like SQL99's LATERAL
|
||||||
|
* construct to ensure that the values would be available while
|
||||||
|
* evaluating the VALUES RTE. This is a shame. FIXME
|
||||||
|
*/
|
||||||
|
if (contain_vars_of_level((Node *) exprsLists, 0))
|
||||||
|
ereport(ERROR,
|
||||||
|
(errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
|
||||||
|
errmsg("VALUES must not contain OLD or NEW references")));
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Generate the VALUES RTE
|
||||||
|
*/
|
||||||
|
rte = addRangeTableEntryForValues(pstate, exprsLists, NULL, true);
|
||||||
|
rtr = makeNode(RangeTblRef);
|
||||||
|
/* assume new rte is at end */
|
||||||
|
rtr->rtindex = list_length(pstate->p_rtable);
|
||||||
|
Assert(rte == rt_fetch(rtr->rtindex, pstate->p_rtable));
|
||||||
|
pstate->p_joinlist = lappend(pstate->p_joinlist, rtr);
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Generate list of Vars referencing the RTE
|
||||||
|
*/
|
||||||
|
expandRTE(rte, rtr->rtindex, 0, false, NULL, &exprList);
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
/*
|
/*----------
|
||||||
* For INSERT ... VALUES, transform the given list of values to form a
|
* Process INSERT ... VALUES with a single VALUES sublist.
|
||||||
* targetlist for the INSERT.
|
* We treat this separately for efficiency and for historical
|
||||||
|
* compatibility --- specifically, allowing table references,
|
||||||
|
* such as
|
||||||
|
* INSERT INTO foo VALUES(bar.*)
|
||||||
|
*
|
||||||
|
* The sublist is just computed directly as the Query's targetlist,
|
||||||
|
* with no VALUES RTE. So it works just like SELECT without FROM.
|
||||||
|
*----------
|
||||||
*/
|
*/
|
||||||
qry->targetList = transformTargetList(pstate, stmt->targetList);
|
List *valuesLists = selectStmt->valuesLists;
|
||||||
|
|
||||||
|
Assert(list_length(valuesLists) == 1);
|
||||||
|
|
||||||
|
/* Do basic expression transformation (same as a ROW() expr) */
|
||||||
|
exprList = transformExpressionList(pstate,
|
||||||
|
(List *) linitial(valuesLists));
|
||||||
|
|
||||||
|
/* Prepare row for assignment to target table */
|
||||||
|
exprList = transformInsertRow(pstate, exprList,
|
||||||
|
stmt->cols,
|
||||||
|
icolumns, attrnos);
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Now we are done with SELECT-like processing, and can get on with
|
* Generate query's target list using the computed list of expressions.
|
||||||
* transforming the target list to match the INSERT target columns.
|
|
||||||
*/
|
|
||||||
|
|
||||||
/* Prepare to assign non-conflicting resnos to resjunk attributes */
|
|
||||||
if (pstate->p_next_resno <= pstate->p_target_relation->rd_rel->relnatts)
|
|
||||||
pstate->p_next_resno = pstate->p_target_relation->rd_rel->relnatts + 1;
|
|
||||||
|
|
||||||
/* Validate stmt->cols list, or build default list if no list given */
|
|
||||||
icolumns = checkInsertTargets(pstate, stmt->cols, &attrnos);
|
|
||||||
|
|
||||||
/*
|
|
||||||
* Prepare columns for assignment to target table.
|
|
||||||
*/
|
*/
|
||||||
|
qry->targetList = NIL;
|
||||||
icols = list_head(icolumns);
|
icols = list_head(icolumns);
|
||||||
attnos = list_head(attrnos);
|
attnos = list_head(attrnos);
|
||||||
foreach(tl, qry->targetList)
|
foreach(lc, exprList)
|
||||||
{
|
{
|
||||||
TargetEntry *tle = (TargetEntry *) lfirst(tl);
|
Expr *expr = (Expr *) lfirst(lc);
|
||||||
ResTarget *col;
|
ResTarget *col;
|
||||||
|
TargetEntry *tle;
|
||||||
if (icols == NULL || attnos == NULL)
|
|
||||||
ereport(ERROR,
|
|
||||||
(errcode(ERRCODE_SYNTAX_ERROR),
|
|
||||||
errmsg("INSERT has more expressions than target columns")));
|
|
||||||
|
|
||||||
col = (ResTarget *) lfirst(icols);
|
col = (ResTarget *) lfirst(icols);
|
||||||
Assert(IsA(col, ResTarget));
|
Assert(IsA(col, ResTarget));
|
||||||
|
|
||||||
Assert(!tle->resjunk);
|
tle = makeTargetEntry(expr,
|
||||||
updateTargetListEntry(pstate, tle, col->name, lfirst_int(attnos),
|
(AttrNumber) lfirst_int(attnos),
|
||||||
col->indirection, col->location);
|
col->name,
|
||||||
|
false);
|
||||||
|
qry->targetList = lappend(qry->targetList, tle);
|
||||||
|
|
||||||
icols = lnext(icols);
|
icols = lnext(icols);
|
||||||
attnos = lnext(attnos);
|
attnos = lnext(attnos);
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
|
||||||
* Ensure that the targetlist has the same number of entries that were
|
|
||||||
* present in the columns list. Don't do the check unless an explicit
|
|
||||||
* columns list was given, though.
|
|
||||||
*/
|
|
||||||
if (stmt->cols != NIL && (icols != NULL || attnos != NULL))
|
|
||||||
ereport(ERROR,
|
|
||||||
(errcode(ERRCODE_SYNTAX_ERROR),
|
|
||||||
errmsg("INSERT has more target columns than expressions")));
|
|
||||||
|
|
||||||
/* done building the range table and jointree */
|
/* done building the range table and jointree */
|
||||||
qry->rtable = pstate->p_rtable;
|
qry->rtable = pstate->p_rtable;
|
||||||
qry->jointree = makeFromExpr(pstate->p_joinlist, NULL);
|
qry->jointree = makeFromExpr(pstate->p_joinlist, NULL);
|
||||||
|
|
||||||
qry->hasSubLinks = pstate->p_hasSubLinks;
|
qry->hasSubLinks = pstate->p_hasSubLinks;
|
||||||
qry->hasAggs = pstate->p_hasAggs;
|
/* aggregates not allowed (but subselects are okay) */
|
||||||
if (pstate->p_hasAggs)
|
if (pstate->p_hasAggs)
|
||||||
parseCheckAggregates(pstate, qry);
|
ereport(ERROR,
|
||||||
|
(errcode(ERRCODE_GROUPING_ERROR),
|
||||||
|
errmsg("cannot use aggregate function in VALUES")));
|
||||||
|
|
||||||
return qry;
|
return qry;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Prepare an INSERT row for assignment to the target table.
|
||||||
|
*
|
||||||
|
* The row might be either a VALUES row, or variables referencing a
|
||||||
|
* sub-SELECT output.
|
||||||
|
*/
|
||||||
|
static List *
|
||||||
|
transformInsertRow(ParseState *pstate, List *exprlist,
|
||||||
|
List *stmtcols, List *icolumns, List *attrnos)
|
||||||
|
{
|
||||||
|
List *result;
|
||||||
|
ListCell *lc;
|
||||||
|
ListCell *icols;
|
||||||
|
ListCell *attnos;
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Check length of expr list. It must not have more expressions than
|
||||||
|
* there are target columns. We allow fewer, but only if no explicit
|
||||||
|
* columns list was given (the remaining columns are implicitly
|
||||||
|
* defaulted). Note we must check this *after* transformation because
|
||||||
|
* that could expand '*' into multiple items.
|
||||||
|
*/
|
||||||
|
if (list_length(exprlist) > list_length(icolumns))
|
||||||
|
ereport(ERROR,
|
||||||
|
(errcode(ERRCODE_SYNTAX_ERROR),
|
||||||
|
errmsg("INSERT has more expressions than target columns")));
|
||||||
|
if (stmtcols != NIL &&
|
||||||
|
list_length(exprlist) < list_length(icolumns))
|
||||||
|
ereport(ERROR,
|
||||||
|
(errcode(ERRCODE_SYNTAX_ERROR),
|
||||||
|
errmsg("INSERT has more target columns than expressions")));
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Prepare columns for assignment to target table.
|
||||||
|
*/
|
||||||
|
result = NIL;
|
||||||
|
icols = list_head(icolumns);
|
||||||
|
attnos = list_head(attrnos);
|
||||||
|
foreach(lc, exprlist)
|
||||||
|
{
|
||||||
|
Expr *expr = (Expr *) lfirst(lc);
|
||||||
|
ResTarget *col;
|
||||||
|
|
||||||
|
col = (ResTarget *) lfirst(icols);
|
||||||
|
Assert(IsA(col, ResTarget));
|
||||||
|
|
||||||
|
expr = transformAssignedExpr(pstate, expr,
|
||||||
|
col->name,
|
||||||
|
lfirst_int(attnos),
|
||||||
|
col->indirection,
|
||||||
|
col->location);
|
||||||
|
|
||||||
|
result = lappend(result, expr);
|
||||||
|
|
||||||
|
icols = lnext(icols);
|
||||||
|
attnos = lnext(attnos);
|
||||||
|
}
|
||||||
|
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* transformCreateStmt -
|
* transformCreateStmt -
|
||||||
* transforms the "create table" statement
|
* transforms the "create table" statement
|
||||||
@ -1933,6 +2101,187 @@ transformSelectStmt(ParseState *pstate, SelectStmt *stmt)
|
|||||||
return qry;
|
return qry;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* transformValuesClause -
|
||||||
|
* transforms a VALUES clause that's being used as a standalone SELECT
|
||||||
|
*
|
||||||
|
* We build a Query containing a VALUES RTE, rather as if one had written
|
||||||
|
* SELECT * FROM (VALUES ...)
|
||||||
|
*/
|
||||||
|
static Query *
|
||||||
|
transformValuesClause(ParseState *pstate, SelectStmt *stmt)
|
||||||
|
{
|
||||||
|
Query *qry = makeNode(Query);
|
||||||
|
List *exprsLists = NIL;
|
||||||
|
List **coltype_lists = NULL;
|
||||||
|
Oid *coltypes = NULL;
|
||||||
|
int sublist_length = -1;
|
||||||
|
List *newExprsLists;
|
||||||
|
RangeTblEntry *rte;
|
||||||
|
RangeTblRef *rtr;
|
||||||
|
ListCell *lc;
|
||||||
|
ListCell *lc2;
|
||||||
|
int i;
|
||||||
|
|
||||||
|
qry->commandType = CMD_SELECT;
|
||||||
|
|
||||||
|
/* Most SELECT stuff doesn't apply in a VALUES clause */
|
||||||
|
Assert(stmt->distinctClause == NIL);
|
||||||
|
Assert(stmt->into == NULL);
|
||||||
|
Assert(stmt->intoColNames == NIL);
|
||||||
|
Assert(stmt->targetList == NIL);
|
||||||
|
Assert(stmt->fromClause == NIL);
|
||||||
|
Assert(stmt->whereClause == NULL);
|
||||||
|
Assert(stmt->groupClause == NIL);
|
||||||
|
Assert(stmt->havingClause == NULL);
|
||||||
|
Assert(stmt->op == SETOP_NONE);
|
||||||
|
|
||||||
|
/*
|
||||||
|
* For each row of VALUES, transform the raw expressions and gather
|
||||||
|
* type information. This is also a handy place to reject DEFAULT
|
||||||
|
* nodes, which the grammar allows for simplicity.
|
||||||
|
*/
|
||||||
|
foreach(lc, stmt->valuesLists)
|
||||||
|
{
|
||||||
|
List *sublist = (List *) lfirst(lc);
|
||||||
|
|
||||||
|
/* Do basic expression transformation (same as a ROW() expr) */
|
||||||
|
sublist = transformExpressionList(pstate, sublist);
|
||||||
|
|
||||||
|
/*
|
||||||
|
* All the sublists must be the same length, *after* transformation
|
||||||
|
* (which might expand '*' into multiple items). The VALUES RTE
|
||||||
|
* can't handle anything different.
|
||||||
|
*/
|
||||||
|
if (sublist_length < 0)
|
||||||
|
{
|
||||||
|
/* Remember post-transformation length of first sublist */
|
||||||
|
sublist_length = list_length(sublist);
|
||||||
|
/* and allocate arrays for column-type info */
|
||||||
|
coltype_lists = (List **) palloc0(sublist_length * sizeof(List *));
|
||||||
|
coltypes = (Oid *) palloc0(sublist_length * sizeof(Oid));
|
||||||
|
}
|
||||||
|
else if (sublist_length != list_length(sublist))
|
||||||
|
{
|
||||||
|
ereport(ERROR,
|
||||||
|
(errcode(ERRCODE_SYNTAX_ERROR),
|
||||||
|
errmsg("VALUES lists must all be the same length")));
|
||||||
|
}
|
||||||
|
|
||||||
|
exprsLists = lappend(exprsLists, sublist);
|
||||||
|
|
||||||
|
i = 0;
|
||||||
|
foreach(lc2, sublist)
|
||||||
|
{
|
||||||
|
Node *col = (Node *) lfirst(lc2);
|
||||||
|
|
||||||
|
if (IsA(col, SetToDefault))
|
||||||
|
ereport(ERROR,
|
||||||
|
(errcode(ERRCODE_SYNTAX_ERROR),
|
||||||
|
errmsg("DEFAULT can only appear in a VALUES list within INSERT")));
|
||||||
|
coltype_lists[i] = lappend_oid(coltype_lists[i], exprType(col));
|
||||||
|
i++;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Now resolve the common types of the columns, and coerce everything
|
||||||
|
* to those types.
|
||||||
|
*/
|
||||||
|
for (i = 0; i < sublist_length; i++)
|
||||||
|
{
|
||||||
|
coltypes[i] = select_common_type(coltype_lists[i], "VALUES");
|
||||||
|
}
|
||||||
|
|
||||||
|
newExprsLists = NIL;
|
||||||
|
foreach(lc, exprsLists)
|
||||||
|
{
|
||||||
|
List *sublist = (List *) lfirst(lc);
|
||||||
|
List *newsublist = NIL;
|
||||||
|
|
||||||
|
i = 0;
|
||||||
|
foreach(lc2, sublist)
|
||||||
|
{
|
||||||
|
Node *col = (Node *) lfirst(lc2);
|
||||||
|
|
||||||
|
col = coerce_to_common_type(pstate, col, coltypes[i], "VALUES");
|
||||||
|
newsublist = lappend(newsublist, col);
|
||||||
|
i++;
|
||||||
|
}
|
||||||
|
|
||||||
|
newExprsLists = lappend(newExprsLists, newsublist);
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Generate the VALUES RTE
|
||||||
|
*/
|
||||||
|
rte = addRangeTableEntryForValues(pstate, newExprsLists, NULL, true);
|
||||||
|
rtr = makeNode(RangeTblRef);
|
||||||
|
/* assume new rte is at end */
|
||||||
|
rtr->rtindex = list_length(pstate->p_rtable);
|
||||||
|
Assert(rte == rt_fetch(rtr->rtindex, pstate->p_rtable));
|
||||||
|
pstate->p_joinlist = lappend(pstate->p_joinlist, rtr);
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Generate a targetlist as though expanding "*"
|
||||||
|
*/
|
||||||
|
Assert(pstate->p_next_resno == 1);
|
||||||
|
qry->targetList = expandRelAttrs(pstate, rte, rtr->rtindex, 0);
|
||||||
|
|
||||||
|
/*
|
||||||
|
* The grammar does allow attaching ORDER BY, LIMIT, and FOR UPDATE
|
||||||
|
* to a VALUES, so cope.
|
||||||
|
*/
|
||||||
|
qry->sortClause = transformSortClause(pstate,
|
||||||
|
stmt->sortClause,
|
||||||
|
&qry->targetList,
|
||||||
|
true /* fix unknowns */ );
|
||||||
|
|
||||||
|
qry->limitOffset = transformLimitClause(pstate, stmt->limitOffset,
|
||||||
|
"OFFSET");
|
||||||
|
qry->limitCount = transformLimitClause(pstate, stmt->limitCount,
|
||||||
|
"LIMIT");
|
||||||
|
|
||||||
|
if (stmt->lockingClause)
|
||||||
|
ereport(ERROR,
|
||||||
|
(errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
|
||||||
|
errmsg("SELECT FOR UPDATE/SHARE cannot be applied to VALUES")));
|
||||||
|
|
||||||
|
/*
|
||||||
|
* There mustn't have been any table references in the expressions,
|
||||||
|
* else strange things would happen, like Cartesian products of
|
||||||
|
* those tables with the VALUES list. We have to check this after
|
||||||
|
* parsing ORDER BY et al since those could insert more junk.
|
||||||
|
*/
|
||||||
|
if (list_length(pstate->p_joinlist) != 1)
|
||||||
|
ereport(ERROR,
|
||||||
|
(errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
|
||||||
|
errmsg("VALUES must not contain table references")));
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Another thing we can't currently support is NEW/OLD references
|
||||||
|
* in rules --- seems we'd need something like SQL99's LATERAL
|
||||||
|
* construct to ensure that the values would be available while
|
||||||
|
* evaluating the VALUES RTE. This is a shame. FIXME
|
||||||
|
*/
|
||||||
|
if (contain_vars_of_level((Node *) newExprsLists, 0))
|
||||||
|
ereport(ERROR,
|
||||||
|
(errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
|
||||||
|
errmsg("VALUES must not contain OLD or NEW references")));
|
||||||
|
|
||||||
|
qry->rtable = pstate->p_rtable;
|
||||||
|
qry->jointree = makeFromExpr(pstate->p_joinlist, NULL);
|
||||||
|
|
||||||
|
qry->hasSubLinks = pstate->p_hasSubLinks;
|
||||||
|
/* aggregates not allowed (but subselects are okay) */
|
||||||
|
if (pstate->p_hasAggs)
|
||||||
|
ereport(ERROR,
|
||||||
|
(errcode(ERRCODE_GROUPING_ERROR),
|
||||||
|
errmsg("cannot use aggregate function in VALUES")));
|
||||||
|
|
||||||
|
return qry;
|
||||||
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* transformSetOperationsStmt -
|
* transformSetOperationsStmt -
|
||||||
* transforms a set-operations tree
|
* transforms a set-operations tree
|
||||||
@ -2931,6 +3280,11 @@ transformLockingClause(Query *qry, LockingClause *lc)
|
|||||||
(errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
|
(errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
|
||||||
errmsg("SELECT FOR UPDATE/SHARE cannot be applied to a function")));
|
errmsg("SELECT FOR UPDATE/SHARE cannot be applied to a function")));
|
||||||
break;
|
break;
|
||||||
|
case RTE_VALUES:
|
||||||
|
ereport(ERROR,
|
||||||
|
(errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
|
||||||
|
errmsg("SELECT FOR UPDATE/SHARE cannot be applied to VALUES")));
|
||||||
|
break;
|
||||||
default:
|
default:
|
||||||
elog(ERROR, "unrecognized RTE type: %d",
|
elog(ERROR, "unrecognized RTE type: %d",
|
||||||
(int) rte->rtekind);
|
(int) rte->rtekind);
|
||||||
|
@ -11,7 +11,7 @@
|
|||||||
*
|
*
|
||||||
*
|
*
|
||||||
* IDENTIFICATION
|
* IDENTIFICATION
|
||||||
* $PostgreSQL: pgsql/src/backend/parser/gram.y,v 2.553 2006/07/31 01:16:37 tgl Exp $
|
* $PostgreSQL: pgsql/src/backend/parser/gram.y,v 2.554 2006/08/02 01:59:46 joe Exp $
|
||||||
*
|
*
|
||||||
* HISTORY
|
* HISTORY
|
||||||
* AUTHOR DATE MAJOR EVENT
|
* AUTHOR DATE MAJOR EVENT
|
||||||
@ -173,7 +173,7 @@ static void doNegateFloat(Value *v);
|
|||||||
DropOwnedStmt ReassignOwnedStmt
|
DropOwnedStmt ReassignOwnedStmt
|
||||||
|
|
||||||
%type <node> select_no_parens select_with_parens select_clause
|
%type <node> select_no_parens select_with_parens select_clause
|
||||||
simple_select
|
simple_select values_clause
|
||||||
|
|
||||||
%type <node> alter_column_default opclass_item alter_using
|
%type <node> alter_column_default opclass_item alter_using
|
||||||
%type <ival> add_drop
|
%type <ival> add_drop
|
||||||
@ -238,7 +238,7 @@ static void doNegateFloat(Value *v);
|
|||||||
qualified_name_list any_name any_name_list
|
qualified_name_list any_name any_name_list
|
||||||
any_operator expr_list attrs
|
any_operator expr_list attrs
|
||||||
target_list update_target_list insert_column_list
|
target_list update_target_list insert_column_list
|
||||||
insert_target_list def_list indirection opt_indirection
|
values_list def_list indirection opt_indirection
|
||||||
group_clause TriggerFuncArgs select_limit
|
group_clause TriggerFuncArgs select_limit
|
||||||
opt_select_limit opclass_item_list
|
opt_select_limit opclass_item_list
|
||||||
transaction_mode_list_or_empty
|
transaction_mode_list_or_empty
|
||||||
@ -299,7 +299,7 @@ static void doNegateFloat(Value *v);
|
|||||||
%type <list> when_clause_list
|
%type <list> when_clause_list
|
||||||
%type <ival> sub_type
|
%type <ival> sub_type
|
||||||
%type <list> OptCreateAs CreateAsList
|
%type <list> OptCreateAs CreateAsList
|
||||||
%type <node> CreateAsElement
|
%type <node> CreateAsElement values_item
|
||||||
%type <value> NumericOnly FloatOnly IntegerOnly
|
%type <value> NumericOnly FloatOnly IntegerOnly
|
||||||
%type <alias> alias_clause
|
%type <alias> alias_clause
|
||||||
%type <sortby> sortby
|
%type <sortby> sortby
|
||||||
@ -308,7 +308,7 @@ static void doNegateFloat(Value *v);
|
|||||||
%type <jexpr> joined_table
|
%type <jexpr> joined_table
|
||||||
%type <range> relation_expr
|
%type <range> relation_expr
|
||||||
%type <range> relation_expr_opt_alias
|
%type <range> relation_expr_opt_alias
|
||||||
%type <target> target_el insert_target_el update_target_el insert_column_item
|
%type <target> target_el update_target_el insert_column_item
|
||||||
|
|
||||||
%type <typnam> Typename SimpleTypename ConstTypename
|
%type <typnam> Typename SimpleTypename ConstTypename
|
||||||
GenericType Numeric opt_float
|
GenericType Numeric opt_float
|
||||||
@ -5342,41 +5342,24 @@ InsertStmt:
|
|||||||
;
|
;
|
||||||
|
|
||||||
insert_rest:
|
insert_rest:
|
||||||
VALUES '(' insert_target_list ')'
|
SelectStmt
|
||||||
{
|
{
|
||||||
$$ = makeNode(InsertStmt);
|
$$ = makeNode(InsertStmt);
|
||||||
$$->cols = NIL;
|
$$->cols = NIL;
|
||||||
$$->targetList = $3;
|
|
||||||
$$->selectStmt = NULL;
|
|
||||||
}
|
|
||||||
| DEFAULT VALUES
|
|
||||||
{
|
|
||||||
$$ = makeNode(InsertStmt);
|
|
||||||
$$->cols = NIL;
|
|
||||||
$$->targetList = NIL;
|
|
||||||
$$->selectStmt = NULL;
|
|
||||||
}
|
|
||||||
| SelectStmt
|
|
||||||
{
|
|
||||||
$$ = makeNode(InsertStmt);
|
|
||||||
$$->cols = NIL;
|
|
||||||
$$->targetList = NIL;
|
|
||||||
$$->selectStmt = $1;
|
$$->selectStmt = $1;
|
||||||
}
|
}
|
||||||
| '(' insert_column_list ')' VALUES '(' insert_target_list ')'
|
|
||||||
{
|
|
||||||
$$ = makeNode(InsertStmt);
|
|
||||||
$$->cols = $2;
|
|
||||||
$$->targetList = $6;
|
|
||||||
$$->selectStmt = NULL;
|
|
||||||
}
|
|
||||||
| '(' insert_column_list ')' SelectStmt
|
| '(' insert_column_list ')' SelectStmt
|
||||||
{
|
{
|
||||||
$$ = makeNode(InsertStmt);
|
$$ = makeNode(InsertStmt);
|
||||||
$$->cols = $2;
|
$$->cols = $2;
|
||||||
$$->targetList = NIL;
|
|
||||||
$$->selectStmt = $4;
|
$$->selectStmt = $4;
|
||||||
}
|
}
|
||||||
|
| DEFAULT VALUES
|
||||||
|
{
|
||||||
|
$$ = makeNode(InsertStmt);
|
||||||
|
$$->cols = NIL;
|
||||||
|
$$->selectStmt = NULL;
|
||||||
|
}
|
||||||
;
|
;
|
||||||
|
|
||||||
insert_column_list:
|
insert_column_list:
|
||||||
@ -5629,6 +5612,7 @@ simple_select:
|
|||||||
n->havingClause = $8;
|
n->havingClause = $8;
|
||||||
$$ = (Node *)n;
|
$$ = (Node *)n;
|
||||||
}
|
}
|
||||||
|
| values_clause { $$ = $1; }
|
||||||
| select_clause UNION opt_all select_clause
|
| select_clause UNION opt_all select_clause
|
||||||
{
|
{
|
||||||
$$ = makeSetOp(SETOP_UNION, $3, $1, $4);
|
$$ = makeSetOp(SETOP_UNION, $3, $1, $4);
|
||||||
@ -5848,6 +5832,32 @@ locked_rels_list:
|
|||||||
| /* EMPTY */ { $$ = NIL; }
|
| /* EMPTY */ { $$ = NIL; }
|
||||||
;
|
;
|
||||||
|
|
||||||
|
|
||||||
|
values_clause:
|
||||||
|
VALUES '(' values_list ')'
|
||||||
|
{
|
||||||
|
SelectStmt *n = makeNode(SelectStmt);
|
||||||
|
n->valuesLists = list_make1($3);
|
||||||
|
$$ = (Node *) n;
|
||||||
|
}
|
||||||
|
| values_clause ',' '(' values_list ')'
|
||||||
|
{
|
||||||
|
SelectStmt *n = (SelectStmt *) $1;
|
||||||
|
n->valuesLists = lappend(n->valuesLists, $4);
|
||||||
|
$$ = (Node *) n;
|
||||||
|
}
|
||||||
|
;
|
||||||
|
|
||||||
|
values_list: values_item { $$ = list_make1($1); }
|
||||||
|
| values_list ',' values_item { $$ = lappend($1, $3); }
|
||||||
|
;
|
||||||
|
|
||||||
|
values_item:
|
||||||
|
a_expr { $$ = (Node *) $1; }
|
||||||
|
| DEFAULT { $$ = (Node *) makeNode(SetToDefault); }
|
||||||
|
;
|
||||||
|
|
||||||
|
|
||||||
/*****************************************************************************
|
/*****************************************************************************
|
||||||
*
|
*
|
||||||
* clauses common to all Optimizable Stmts:
|
* clauses common to all Optimizable Stmts:
|
||||||
@ -5937,10 +5947,17 @@ table_ref: relation_expr
|
|||||||
* However, it does seem like a good idea to emit
|
* However, it does seem like a good idea to emit
|
||||||
* an error message that's better than "syntax error".
|
* an error message that's better than "syntax error".
|
||||||
*/
|
*/
|
||||||
ereport(ERROR,
|
if (IsA($1, SelectStmt) &&
|
||||||
(errcode(ERRCODE_SYNTAX_ERROR),
|
((SelectStmt *) $1)->valuesLists)
|
||||||
errmsg("subquery in FROM must have an alias"),
|
ereport(ERROR,
|
||||||
errhint("For example, FROM (SELECT ...) [AS] foo.")));
|
(errcode(ERRCODE_SYNTAX_ERROR),
|
||||||
|
errmsg("VALUES in FROM must have an alias"),
|
||||||
|
errhint("For example, FROM (VALUES ...) [AS] foo.")));
|
||||||
|
else
|
||||||
|
ereport(ERROR,
|
||||||
|
(errcode(ERRCODE_SYNTAX_ERROR),
|
||||||
|
errmsg("subquery in FROM must have an alias"),
|
||||||
|
errhint("For example, FROM (SELECT ...) [AS] foo.")));
|
||||||
$$ = NULL;
|
$$ = NULL;
|
||||||
}
|
}
|
||||||
| select_with_parens alias_clause
|
| select_with_parens alias_clause
|
||||||
@ -8176,30 +8193,6 @@ update_target_el:
|
|||||||
|
|
||||||
;
|
;
|
||||||
|
|
||||||
insert_target_list:
|
|
||||||
insert_target_el { $$ = list_make1($1); }
|
|
||||||
| insert_target_list ',' insert_target_el { $$ = lappend($1, $3); }
|
|
||||||
;
|
|
||||||
|
|
||||||
insert_target_el:
|
|
||||||
a_expr
|
|
||||||
{
|
|
||||||
$$ = makeNode(ResTarget);
|
|
||||||
$$->name = NULL;
|
|
||||||
$$->indirection = NIL;
|
|
||||||
$$->val = (Node *)$1;
|
|
||||||
$$->location = @1;
|
|
||||||
}
|
|
||||||
| DEFAULT
|
|
||||||
{
|
|
||||||
$$ = makeNode(ResTarget);
|
|
||||||
$$->name = NULL;
|
|
||||||
$$->indirection = NIL;
|
|
||||||
$$->val = (Node *) makeNode(SetToDefault);
|
|
||||||
$$->location = @1;
|
|
||||||
}
|
|
||||||
;
|
|
||||||
|
|
||||||
|
|
||||||
/*****************************************************************************
|
/*****************************************************************************
|
||||||
*
|
*
|
||||||
@ -8656,7 +8649,6 @@ unreserved_keyword:
|
|||||||
| VACUUM
|
| VACUUM
|
||||||
| VALID
|
| VALID
|
||||||
| VALIDATOR
|
| VALIDATOR
|
||||||
| VALUES
|
|
||||||
| VARYING
|
| VARYING
|
||||||
| VIEW
|
| VIEW
|
||||||
| VOLATILE
|
| VOLATILE
|
||||||
@ -8715,6 +8707,7 @@ col_name_keyword:
|
|||||||
| TIMESTAMP
|
| TIMESTAMP
|
||||||
| TREAT
|
| TREAT
|
||||||
| TRIM
|
| TRIM
|
||||||
|
| VALUES
|
||||||
| VARCHAR
|
| VARCHAR
|
||||||
;
|
;
|
||||||
|
|
||||||
|
@ -8,7 +8,7 @@
|
|||||||
*
|
*
|
||||||
*
|
*
|
||||||
* IDENTIFICATION
|
* IDENTIFICATION
|
||||||
* $PostgreSQL: pgsql/src/backend/parser/parse_expr.c,v 1.195 2006/07/14 14:52:22 momjian Exp $
|
* $PostgreSQL: pgsql/src/backend/parser/parse_expr.c,v 1.196 2006/08/02 01:59:46 joe Exp $
|
||||||
*
|
*
|
||||||
*-------------------------------------------------------------------------
|
*-------------------------------------------------------------------------
|
||||||
*/
|
*/
|
||||||
@ -1281,56 +1281,9 @@ static Node *
|
|||||||
transformRowExpr(ParseState *pstate, RowExpr *r)
|
transformRowExpr(ParseState *pstate, RowExpr *r)
|
||||||
{
|
{
|
||||||
RowExpr *newr = makeNode(RowExpr);
|
RowExpr *newr = makeNode(RowExpr);
|
||||||
List *newargs = NIL;
|
|
||||||
ListCell *arg;
|
|
||||||
|
|
||||||
/* Transform the field expressions */
|
/* Transform the field expressions */
|
||||||
foreach(arg, r->args)
|
newr->args = transformExpressionList(pstate, r->args);
|
||||||
{
|
|
||||||
Node *e = (Node *) lfirst(arg);
|
|
||||||
Node *newe;
|
|
||||||
|
|
||||||
/*
|
|
||||||
* Check for "something.*". Depending on the complexity of the
|
|
||||||
* "something", the star could appear as the last name in ColumnRef,
|
|
||||||
* or as the last indirection item in A_Indirection.
|
|
||||||
*/
|
|
||||||
if (IsA(e, ColumnRef))
|
|
||||||
{
|
|
||||||
ColumnRef *cref = (ColumnRef *) e;
|
|
||||||
|
|
||||||
if (strcmp(strVal(llast(cref->fields)), "*") == 0)
|
|
||||||
{
|
|
||||||
/* It is something.*, expand into multiple items */
|
|
||||||
newargs = list_concat(newargs,
|
|
||||||
ExpandColumnRefStar(pstate, cref,
|
|
||||||
false));
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else if (IsA(e, A_Indirection))
|
|
||||||
{
|
|
||||||
A_Indirection *ind = (A_Indirection *) e;
|
|
||||||
Node *lastitem = llast(ind->indirection);
|
|
||||||
|
|
||||||
if (IsA(lastitem, String) &&
|
|
||||||
strcmp(strVal(lastitem), "*") == 0)
|
|
||||||
{
|
|
||||||
/* It is something.*, expand into multiple items */
|
|
||||||
newargs = list_concat(newargs,
|
|
||||||
ExpandIndirectionStar(pstate, ind,
|
|
||||||
false));
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/*
|
|
||||||
* Not "something.*", so transform as a single expression
|
|
||||||
*/
|
|
||||||
newe = transformExpr(pstate, e);
|
|
||||||
newargs = lappend(newargs, newe);
|
|
||||||
}
|
|
||||||
newr->args = newargs;
|
|
||||||
|
|
||||||
/* Barring later casting, we consider the type RECORD */
|
/* Barring later casting, we consider the type RECORD */
|
||||||
newr->row_typeid = RECORDOID;
|
newr->row_typeid = RECORDOID;
|
||||||
@ -1526,6 +1479,15 @@ transformWholeRowRef(ParseState *pstate, char *schemaname, char *relname,
|
|||||||
sublevels_up);
|
sublevels_up);
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
|
case RTE_VALUES:
|
||||||
|
toid = RECORDOID;
|
||||||
|
/* returns composite; same as relation case */
|
||||||
|
result = (Node *) makeVar(vnum,
|
||||||
|
InvalidAttrNumber,
|
||||||
|
toid,
|
||||||
|
-1,
|
||||||
|
sublevels_up);
|
||||||
|
break;
|
||||||
default:
|
default:
|
||||||
|
|
||||||
/*
|
/*
|
||||||
|
@ -8,7 +8,7 @@
|
|||||||
*
|
*
|
||||||
*
|
*
|
||||||
* IDENTIFICATION
|
* IDENTIFICATION
|
||||||
* $PostgreSQL: pgsql/src/backend/parser/parse_node.c,v 1.93 2006/07/14 14:52:22 momjian Exp $
|
* $PostgreSQL: pgsql/src/backend/parser/parse_node.c,v 1.94 2006/08/02 01:59:47 joe Exp $
|
||||||
*
|
*
|
||||||
*-------------------------------------------------------------------------
|
*-------------------------------------------------------------------------
|
||||||
*/
|
*/
|
||||||
@ -258,7 +258,7 @@ transformArraySubscripts(ParseState *pstate,
|
|||||||
|
|
||||||
/*
|
/*
|
||||||
* If doing an array store, coerce the source value to the right type.
|
* If doing an array store, coerce the source value to the right type.
|
||||||
* (This should agree with the coercion done by updateTargetListEntry.)
|
* (This should agree with the coercion done by transformAssignedExpr.)
|
||||||
*/
|
*/
|
||||||
if (assignFrom != NULL)
|
if (assignFrom != NULL)
|
||||||
{
|
{
|
||||||
|
@ -8,7 +8,7 @@
|
|||||||
*
|
*
|
||||||
*
|
*
|
||||||
* IDENTIFICATION
|
* IDENTIFICATION
|
||||||
* $PostgreSQL: pgsql/src/backend/parser/parse_relation.c,v 1.123 2006/04/30 18:30:39 tgl Exp $
|
* $PostgreSQL: pgsql/src/backend/parser/parse_relation.c,v 1.124 2006/08/02 01:59:47 joe Exp $
|
||||||
*
|
*
|
||||||
*-------------------------------------------------------------------------
|
*-------------------------------------------------------------------------
|
||||||
*/
|
*/
|
||||||
@ -940,6 +940,75 @@ addRangeTableEntryForFunction(ParseState *pstate,
|
|||||||
return rte;
|
return rte;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Add an entry for a VALUES list to the pstate's range table (p_rtable).
|
||||||
|
*
|
||||||
|
* This is much like addRangeTableEntry() except that it makes a values RTE.
|
||||||
|
*/
|
||||||
|
RangeTblEntry *
|
||||||
|
addRangeTableEntryForValues(ParseState *pstate,
|
||||||
|
List *exprs,
|
||||||
|
Alias *alias,
|
||||||
|
bool inFromCl)
|
||||||
|
{
|
||||||
|
RangeTblEntry *rte = makeNode(RangeTblEntry);
|
||||||
|
char *refname = alias ? alias->aliasname : pstrdup("*VALUES*");
|
||||||
|
Alias *eref;
|
||||||
|
int numaliases;
|
||||||
|
int numcolumns;
|
||||||
|
|
||||||
|
rte->rtekind = RTE_VALUES;
|
||||||
|
rte->relid = InvalidOid;
|
||||||
|
rte->subquery = NULL;
|
||||||
|
rte->values_lists = exprs;
|
||||||
|
rte->alias = alias;
|
||||||
|
|
||||||
|
eref = alias ? copyObject(alias) : makeAlias(refname, NIL);
|
||||||
|
|
||||||
|
/* fill in any unspecified alias columns */
|
||||||
|
numcolumns = list_length((List *) linitial(exprs));
|
||||||
|
numaliases = list_length(eref->colnames);
|
||||||
|
while (numaliases < numcolumns)
|
||||||
|
{
|
||||||
|
char attrname[64];
|
||||||
|
|
||||||
|
numaliases++;
|
||||||
|
snprintf(attrname, sizeof(attrname), "column%d", numaliases);
|
||||||
|
eref->colnames = lappend(eref->colnames,
|
||||||
|
makeString(pstrdup(attrname)));
|
||||||
|
}
|
||||||
|
if (numcolumns < numaliases)
|
||||||
|
ereport(ERROR,
|
||||||
|
(errcode(ERRCODE_INVALID_COLUMN_REFERENCE),
|
||||||
|
errmsg("VALUES lists \"%s\" have %d columns available but %d columns specified",
|
||||||
|
refname, numcolumns, numaliases)));
|
||||||
|
|
||||||
|
rte->eref = eref;
|
||||||
|
|
||||||
|
/*----------
|
||||||
|
* Flags:
|
||||||
|
* - this RTE should be expanded to include descendant tables,
|
||||||
|
* - this RTE is in the FROM clause,
|
||||||
|
* - this RTE should be checked for appropriate access rights.
|
||||||
|
*
|
||||||
|
* Subqueries are never checked for access rights.
|
||||||
|
*----------
|
||||||
|
*/
|
||||||
|
rte->inh = false; /* never true for values RTEs */
|
||||||
|
rte->inFromCl = inFromCl;
|
||||||
|
rte->requiredPerms = 0;
|
||||||
|
rte->checkAsUser = InvalidOid;
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Add completed RTE to pstate's range table list, but not to join list
|
||||||
|
* nor namespace --- caller must do that if appropriate.
|
||||||
|
*/
|
||||||
|
if (pstate != NULL)
|
||||||
|
pstate->p_rtable = lappend(pstate->p_rtable, rte);
|
||||||
|
|
||||||
|
return rte;
|
||||||
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Add an entry for a join to the pstate's range table (p_rtable).
|
* Add an entry for a join to the pstate's range table (p_rtable).
|
||||||
*
|
*
|
||||||
@ -1233,6 +1302,41 @@ expandRTE(RangeTblEntry *rte, int rtindex, int sublevels_up,
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
|
case RTE_VALUES:
|
||||||
|
{
|
||||||
|
/* Values RTE */
|
||||||
|
ListCell *aliasp_item = list_head(rte->eref->colnames);
|
||||||
|
ListCell *lc;
|
||||||
|
|
||||||
|
varattno = 0;
|
||||||
|
foreach(lc, (List *) linitial(rte->values_lists))
|
||||||
|
{
|
||||||
|
Node *col = (Node *) lfirst(lc);
|
||||||
|
|
||||||
|
varattno++;
|
||||||
|
if (colnames)
|
||||||
|
{
|
||||||
|
/* Assume there is one alias per column */
|
||||||
|
char *label = strVal(lfirst(aliasp_item));
|
||||||
|
|
||||||
|
*colnames = lappend(*colnames,
|
||||||
|
makeString(pstrdup(label)));
|
||||||
|
aliasp_item = lnext(aliasp_item);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (colvars)
|
||||||
|
{
|
||||||
|
Var *varnode;
|
||||||
|
|
||||||
|
varnode = makeVar(rtindex, varattno,
|
||||||
|
exprType(col),
|
||||||
|
exprTypmod(col),
|
||||||
|
sublevels_up);
|
||||||
|
*colvars = lappend(*colvars, varnode);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
break;
|
||||||
case RTE_JOIN:
|
case RTE_JOIN:
|
||||||
{
|
{
|
||||||
/* Join RTE */
|
/* Join RTE */
|
||||||
@ -1569,6 +1673,20 @@ get_rte_attribute_type(RangeTblEntry *rte, AttrNumber attnum,
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
|
case RTE_VALUES:
|
||||||
|
{
|
||||||
|
/* Values RTE --- get type info from first sublist */
|
||||||
|
List *collist = (List *) linitial(rte->values_lists);
|
||||||
|
Node *col;
|
||||||
|
|
||||||
|
if (attnum < 1 || attnum > list_length(collist))
|
||||||
|
elog(ERROR, "values list %s does not have attribute %d",
|
||||||
|
rte->eref->aliasname, attnum);
|
||||||
|
col = (Node *) list_nth(collist, attnum-1);
|
||||||
|
*vartype = exprType(col);
|
||||||
|
*vartypmod = exprTypmod(col);
|
||||||
|
}
|
||||||
|
break;
|
||||||
case RTE_JOIN:
|
case RTE_JOIN:
|
||||||
{
|
{
|
||||||
/*
|
/*
|
||||||
@ -1619,7 +1737,8 @@ get_rte_attribute_is_dropped(RangeTblEntry *rte, AttrNumber attnum)
|
|||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
case RTE_SUBQUERY:
|
case RTE_SUBQUERY:
|
||||||
/* Subselect RTEs never have dropped columns */
|
case RTE_VALUES:
|
||||||
|
/* Subselect and Values RTEs never have dropped columns */
|
||||||
result = false;
|
result = false;
|
||||||
break;
|
break;
|
||||||
case RTE_JOIN:
|
case RTE_JOIN:
|
||||||
|
@ -8,7 +8,7 @@
|
|||||||
*
|
*
|
||||||
*
|
*
|
||||||
* IDENTIFICATION
|
* IDENTIFICATION
|
||||||
* $PostgreSQL: pgsql/src/backend/parser/parse_target.c,v 1.146 2006/07/14 14:52:22 momjian Exp $
|
* $PostgreSQL: pgsql/src/backend/parser/parse_target.c,v 1.147 2006/08/02 01:59:47 joe Exp $
|
||||||
*
|
*
|
||||||
*-------------------------------------------------------------------------
|
*-------------------------------------------------------------------------
|
||||||
*/
|
*/
|
||||||
@ -42,7 +42,11 @@ static Node *transformAssignmentIndirection(ParseState *pstate,
|
|||||||
ListCell *indirection,
|
ListCell *indirection,
|
||||||
Node *rhs,
|
Node *rhs,
|
||||||
int location);
|
int location);
|
||||||
|
static List *ExpandColumnRefStar(ParseState *pstate, ColumnRef *cref,
|
||||||
|
bool targetlist);
|
||||||
static List *ExpandAllTables(ParseState *pstate);
|
static List *ExpandAllTables(ParseState *pstate);
|
||||||
|
static List *ExpandIndirectionStar(ParseState *pstate, A_Indirection *ind,
|
||||||
|
bool targetlist);
|
||||||
static int FigureColnameInternal(Node *node, char **name);
|
static int FigureColnameInternal(Node *node, char **name);
|
||||||
|
|
||||||
|
|
||||||
@ -151,6 +155,69 @@ transformTargetList(ParseState *pstate, List *targetlist)
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/*
|
||||||
|
* transformExpressionList()
|
||||||
|
*
|
||||||
|
* This is the identical transformation to transformTargetList, except that
|
||||||
|
* the input list elements are bare expressions without ResTarget decoration,
|
||||||
|
* and the output elements are likewise just expressions without TargetEntry
|
||||||
|
* decoration. We use this for ROW() and VALUES() constructs.
|
||||||
|
*/
|
||||||
|
List *
|
||||||
|
transformExpressionList(ParseState *pstate, List *exprlist)
|
||||||
|
{
|
||||||
|
List *result = NIL;
|
||||||
|
ListCell *lc;
|
||||||
|
|
||||||
|
foreach(lc, exprlist)
|
||||||
|
{
|
||||||
|
Node *e = (Node *) lfirst(lc);
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Check for "something.*". Depending on the complexity of the
|
||||||
|
* "something", the star could appear as the last name in ColumnRef,
|
||||||
|
* or as the last indirection item in A_Indirection.
|
||||||
|
*/
|
||||||
|
if (IsA(e, ColumnRef))
|
||||||
|
{
|
||||||
|
ColumnRef *cref = (ColumnRef *) e;
|
||||||
|
|
||||||
|
if (strcmp(strVal(llast(cref->fields)), "*") == 0)
|
||||||
|
{
|
||||||
|
/* It is something.*, expand into multiple items */
|
||||||
|
result = list_concat(result,
|
||||||
|
ExpandColumnRefStar(pstate, cref,
|
||||||
|
false));
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else if (IsA(e, A_Indirection))
|
||||||
|
{
|
||||||
|
A_Indirection *ind = (A_Indirection *) e;
|
||||||
|
Node *lastitem = llast(ind->indirection);
|
||||||
|
|
||||||
|
if (IsA(lastitem, String) &&
|
||||||
|
strcmp(strVal(lastitem), "*") == 0)
|
||||||
|
{
|
||||||
|
/* It is something.*, expand into multiple items */
|
||||||
|
result = list_concat(result,
|
||||||
|
ExpandIndirectionStar(pstate, ind,
|
||||||
|
false));
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Not "something.*", so transform as a single expression
|
||||||
|
*/
|
||||||
|
result = lappend(result,
|
||||||
|
transformExpr(pstate, e));
|
||||||
|
}
|
||||||
|
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* markTargetListOrigins()
|
* markTargetListOrigins()
|
||||||
* Mark targetlist columns that are simple Vars with the source
|
* Mark targetlist columns that are simple Vars with the source
|
||||||
@ -229,6 +296,7 @@ markTargetListOrigin(ParseState *pstate, TargetEntry *tle,
|
|||||||
break;
|
break;
|
||||||
case RTE_SPECIAL:
|
case RTE_SPECIAL:
|
||||||
case RTE_FUNCTION:
|
case RTE_FUNCTION:
|
||||||
|
case RTE_VALUES:
|
||||||
/* not a simple relation, leave it unmarked */
|
/* not a simple relation, leave it unmarked */
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
@ -236,23 +304,26 @@ markTargetListOrigin(ParseState *pstate, TargetEntry *tle,
|
|||||||
|
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* updateTargetListEntry()
|
* transformAssignedExpr()
|
||||||
* This is used in INSERT and UPDATE statements only. It prepares a
|
* This is used in INSERT and UPDATE statements only. It prepares an
|
||||||
* TargetEntry for assignment to a column of the target table.
|
* expression for assignment to a column of the target table.
|
||||||
* This includes coercing the given value to the target column's type
|
* This includes coercing the given value to the target column's type
|
||||||
* (if necessary), and dealing with any subfield names or subscripts
|
* (if necessary), and dealing with any subfield names or subscripts
|
||||||
* attached to the target column itself.
|
* attached to the target column itself. The input expression has
|
||||||
|
* already been through transformExpr().
|
||||||
*
|
*
|
||||||
* pstate parse state
|
* pstate parse state
|
||||||
* tle target list entry to be modified
|
* expr expression to be modified
|
||||||
* colname target column name (ie, name of attribute to be assigned to)
|
* colname target column name (ie, name of attribute to be assigned to)
|
||||||
* attrno target attribute number
|
* attrno target attribute number
|
||||||
* indirection subscripts/field names for target column, if any
|
* indirection subscripts/field names for target column, if any
|
||||||
* location error cursor position (should point at column name), or -1
|
* location error cursor position, or -1
|
||||||
|
*
|
||||||
|
* Returns the modified expression.
|
||||||
*/
|
*/
|
||||||
void
|
Expr *
|
||||||
updateTargetListEntry(ParseState *pstate,
|
transformAssignedExpr(ParseState *pstate,
|
||||||
TargetEntry *tle,
|
Expr *expr,
|
||||||
char *colname,
|
char *colname,
|
||||||
int attrno,
|
int attrno,
|
||||||
List *indirection,
|
List *indirection,
|
||||||
@ -281,9 +352,9 @@ updateTargetListEntry(ParseState *pstate,
|
|||||||
* or array element with DEFAULT, since there can't be any default for
|
* or array element with DEFAULT, since there can't be any default for
|
||||||
* portions of a column.
|
* portions of a column.
|
||||||
*/
|
*/
|
||||||
if (tle->expr && IsA(tle->expr, SetToDefault))
|
if (expr && IsA(expr, SetToDefault))
|
||||||
{
|
{
|
||||||
SetToDefault *def = (SetToDefault *) tle->expr;
|
SetToDefault *def = (SetToDefault *) expr;
|
||||||
|
|
||||||
def->typeId = attrtype;
|
def->typeId = attrtype;
|
||||||
def->typeMod = attrtypmod;
|
def->typeMod = attrtypmod;
|
||||||
@ -303,7 +374,7 @@ updateTargetListEntry(ParseState *pstate,
|
|||||||
}
|
}
|
||||||
|
|
||||||
/* Now we can use exprType() safely. */
|
/* Now we can use exprType() safely. */
|
||||||
type_id = exprType((Node *) tle->expr);
|
type_id = exprType((Node *) expr);
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* If there is indirection on the target column, prepare an array or
|
* If there is indirection on the target column, prepare an array or
|
||||||
@ -334,7 +405,7 @@ updateTargetListEntry(ParseState *pstate,
|
|||||||
attrno);
|
attrno);
|
||||||
}
|
}
|
||||||
|
|
||||||
tle->expr = (Expr *)
|
expr = (Expr *)
|
||||||
transformAssignmentIndirection(pstate,
|
transformAssignmentIndirection(pstate,
|
||||||
colVar,
|
colVar,
|
||||||
colname,
|
colname,
|
||||||
@ -342,7 +413,7 @@ updateTargetListEntry(ParseState *pstate,
|
|||||||
attrtype,
|
attrtype,
|
||||||
attrtypmod,
|
attrtypmod,
|
||||||
list_head(indirection),
|
list_head(indirection),
|
||||||
(Node *) tle->expr,
|
(Node *) expr,
|
||||||
location);
|
location);
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
@ -351,13 +422,13 @@ updateTargetListEntry(ParseState *pstate,
|
|||||||
* For normal non-qualified target column, do type checking and
|
* For normal non-qualified target column, do type checking and
|
||||||
* coercion.
|
* coercion.
|
||||||
*/
|
*/
|
||||||
tle->expr = (Expr *)
|
expr = (Expr *)
|
||||||
coerce_to_target_type(pstate,
|
coerce_to_target_type(pstate,
|
||||||
(Node *) tle->expr, type_id,
|
(Node *) expr, type_id,
|
||||||
attrtype, attrtypmod,
|
attrtype, attrtypmod,
|
||||||
COERCION_ASSIGNMENT,
|
COERCION_ASSIGNMENT,
|
||||||
COERCE_IMPLICIT_CAST);
|
COERCE_IMPLICIT_CAST);
|
||||||
if (tle->expr == NULL)
|
if (expr == NULL)
|
||||||
ereport(ERROR,
|
ereport(ERROR,
|
||||||
(errcode(ERRCODE_DATATYPE_MISMATCH),
|
(errcode(ERRCODE_DATATYPE_MISMATCH),
|
||||||
errmsg("column \"%s\" is of type %s"
|
errmsg("column \"%s\" is of type %s"
|
||||||
@ -369,6 +440,41 @@ updateTargetListEntry(ParseState *pstate,
|
|||||||
parser_errposition(pstate, location)));
|
parser_errposition(pstate, location)));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
return expr;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/*
|
||||||
|
* updateTargetListEntry()
|
||||||
|
* This is used in UPDATE statements only. It prepares an UPDATE
|
||||||
|
* TargetEntry for assignment to a column of the target table.
|
||||||
|
* This includes coercing the given value to the target column's type
|
||||||
|
* (if necessary), and dealing with any subfield names or subscripts
|
||||||
|
* attached to the target column itself.
|
||||||
|
*
|
||||||
|
* pstate parse state
|
||||||
|
* tle target list entry to be modified
|
||||||
|
* colname target column name (ie, name of attribute to be assigned to)
|
||||||
|
* attrno target attribute number
|
||||||
|
* indirection subscripts/field names for target column, if any
|
||||||
|
* location error cursor position (should point at column name), or -1
|
||||||
|
*/
|
||||||
|
void
|
||||||
|
updateTargetListEntry(ParseState *pstate,
|
||||||
|
TargetEntry *tle,
|
||||||
|
char *colname,
|
||||||
|
int attrno,
|
||||||
|
List *indirection,
|
||||||
|
int location)
|
||||||
|
{
|
||||||
|
/* Fix up expression as needed */
|
||||||
|
tle->expr = transformAssignedExpr(pstate,
|
||||||
|
tle->expr,
|
||||||
|
colname,
|
||||||
|
attrno,
|
||||||
|
indirection,
|
||||||
|
location);
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Set the resno to identify the target column --- the rewriter and
|
* Set the resno to identify the target column --- the rewriter and
|
||||||
* planner depend on this. We also set the resname to identify the target
|
* planner depend on this. We also set the resname to identify the target
|
||||||
@ -379,6 +485,7 @@ updateTargetListEntry(ParseState *pstate,
|
|||||||
tle->resname = colname;
|
tle->resname = colname;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Process indirection (field selection or subscripting) of the target
|
* Process indirection (field selection or subscripting) of the target
|
||||||
* column in INSERT/UPDATE. This routine recurses for multiple levels
|
* column in INSERT/UPDATE. This routine recurses for multiple levels
|
||||||
@ -701,9 +808,10 @@ checkInsertTargets(ParseState *pstate, List *cols, List **attrnos)
|
|||||||
* This handles the case where '*' appears as the last or only name in a
|
* This handles the case where '*' appears as the last or only name in a
|
||||||
* ColumnRef. The code is shared between the case of foo.* at the top level
|
* ColumnRef. The code is shared between the case of foo.* at the top level
|
||||||
* in a SELECT target list (where we want TargetEntry nodes in the result)
|
* in a SELECT target list (where we want TargetEntry nodes in the result)
|
||||||
* and foo.* in a ROW() construct (where we want just bare expressions).
|
* and foo.* in a ROW() or VALUES() construct (where we want just bare
|
||||||
|
* expressions).
|
||||||
*/
|
*/
|
||||||
List *
|
static List *
|
||||||
ExpandColumnRefStar(ParseState *pstate, ColumnRef *cref,
|
ExpandColumnRefStar(ParseState *pstate, ColumnRef *cref,
|
||||||
bool targetlist)
|
bool targetlist)
|
||||||
{
|
{
|
||||||
@ -836,9 +944,9 @@ ExpandAllTables(ParseState *pstate)
|
|||||||
* This handles the case where '*' appears as the last item in A_Indirection.
|
* This handles the case where '*' appears as the last item in A_Indirection.
|
||||||
* The code is shared between the case of foo.* at the top level in a SELECT
|
* The code is shared between the case of foo.* at the top level in a SELECT
|
||||||
* target list (where we want TargetEntry nodes in the result) and foo.* in
|
* target list (where we want TargetEntry nodes in the result) and foo.* in
|
||||||
* a ROW() construct (where we want just bare expressions).
|
* a ROW() or VALUES() construct (where we want just bare expressions).
|
||||||
*/
|
*/
|
||||||
List *
|
static List *
|
||||||
ExpandIndirectionStar(ParseState *pstate, A_Indirection *ind,
|
ExpandIndirectionStar(ParseState *pstate, A_Indirection *ind,
|
||||||
bool targetlist)
|
bool targetlist)
|
||||||
{
|
{
|
||||||
@ -996,11 +1104,12 @@ expandRecordVariable(ParseState *pstate, Var *var, int levelsup)
|
|||||||
{
|
{
|
||||||
case RTE_RELATION:
|
case RTE_RELATION:
|
||||||
case RTE_SPECIAL:
|
case RTE_SPECIAL:
|
||||||
|
case RTE_VALUES:
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* This case should not occur: a column of a table shouldn't have
|
* This case should not occur: a column of a table or values list
|
||||||
* type RECORD. Fall through and fail (most likely) at the
|
* shouldn't have type RECORD. Fall through and fail
|
||||||
* bottom.
|
* (most likely) at the bottom.
|
||||||
*/
|
*/
|
||||||
break;
|
break;
|
||||||
case RTE_SUBQUERY:
|
case RTE_SUBQUERY:
|
||||||
|
@ -8,7 +8,7 @@
|
|||||||
*
|
*
|
||||||
*
|
*
|
||||||
* IDENTIFICATION
|
* IDENTIFICATION
|
||||||
* $PostgreSQL: pgsql/src/backend/parser/parse_type.c,v 1.82 2006/07/14 14:52:22 momjian Exp $
|
* $PostgreSQL: pgsql/src/backend/parser/parse_type.c,v 1.83 2006/08/02 01:59:47 joe Exp $
|
||||||
*
|
*
|
||||||
*-------------------------------------------------------------------------
|
*-------------------------------------------------------------------------
|
||||||
*/
|
*/
|
||||||
@ -426,6 +426,7 @@ parseTypeString(const char *str, Oid *type_id, int32 *typmod)
|
|||||||
stmt->whereClause != NULL ||
|
stmt->whereClause != NULL ||
|
||||||
stmt->groupClause != NIL ||
|
stmt->groupClause != NIL ||
|
||||||
stmt->havingClause != NULL ||
|
stmt->havingClause != NULL ||
|
||||||
|
stmt->valuesLists != NIL ||
|
||||||
stmt->sortClause != NIL ||
|
stmt->sortClause != NIL ||
|
||||||
stmt->limitOffset != NULL ||
|
stmt->limitOffset != NULL ||
|
||||||
stmt->limitCount != NULL ||
|
stmt->limitCount != NULL ||
|
||||||
|
@ -7,7 +7,7 @@
|
|||||||
* Portions Copyright (c) 1994, Regents of the University of California
|
* Portions Copyright (c) 1994, Regents of the University of California
|
||||||
*
|
*
|
||||||
* IDENTIFICATION
|
* IDENTIFICATION
|
||||||
* $PostgreSQL: pgsql/src/backend/rewrite/rewriteHandler.c,v 1.164 2006/07/14 14:52:22 momjian Exp $
|
* $PostgreSQL: pgsql/src/backend/rewrite/rewriteHandler.c,v 1.165 2006/08/02 01:59:47 joe Exp $
|
||||||
*
|
*
|
||||||
*-------------------------------------------------------------------------
|
*-------------------------------------------------------------------------
|
||||||
*/
|
*/
|
||||||
@ -41,11 +41,14 @@ static Query *rewriteRuleAction(Query *parsetree,
|
|||||||
int rt_index,
|
int rt_index,
|
||||||
CmdType event);
|
CmdType event);
|
||||||
static List *adjustJoinTreeList(Query *parsetree, bool removert, int rt_index);
|
static List *adjustJoinTreeList(Query *parsetree, bool removert, int rt_index);
|
||||||
static void rewriteTargetList(Query *parsetree, Relation target_relation);
|
static void rewriteTargetList(Query *parsetree, Relation target_relation,
|
||||||
|
List **attrno_list);
|
||||||
static TargetEntry *process_matched_tle(TargetEntry *src_tle,
|
static TargetEntry *process_matched_tle(TargetEntry *src_tle,
|
||||||
TargetEntry *prior_tle,
|
TargetEntry *prior_tle,
|
||||||
const char *attrName);
|
const char *attrName);
|
||||||
static Node *get_assignment_input(Node *node);
|
static Node *get_assignment_input(Node *node);
|
||||||
|
static void rewriteValuesRTE(RangeTblEntry *rte, Relation target_relation,
|
||||||
|
List *attrnos);
|
||||||
static void markQueryForLocking(Query *qry, bool forUpdate, bool noWait,
|
static void markQueryForLocking(Query *qry, bool forUpdate, bool noWait,
|
||||||
bool skipOldNew);
|
bool skipOldNew);
|
||||||
static List *matchLocks(CmdType event, RuleLock *rulelocks,
|
static List *matchLocks(CmdType event, RuleLock *rulelocks,
|
||||||
@ -480,9 +483,15 @@ adjustJoinTreeList(Query *parsetree, bool removert, int rt_index)
|
|||||||
* references to NEW.foo will produce wrong or incomplete results. Item 3
|
* references to NEW.foo will produce wrong or incomplete results. Item 3
|
||||||
* is not needed for rewriting, but will be needed by the planner, and we
|
* is not needed for rewriting, but will be needed by the planner, and we
|
||||||
* can do it essentially for free while handling items 1 and 2.
|
* can do it essentially for free while handling items 1 and 2.
|
||||||
|
*
|
||||||
|
* If attrno_list isn't NULL, we return an additional output besides the
|
||||||
|
* rewritten targetlist: an integer list of the assigned-to attnums, in
|
||||||
|
* order of the original tlist's non-junk entries. This is needed for
|
||||||
|
* processing VALUES RTEs.
|
||||||
*/
|
*/
|
||||||
static void
|
static void
|
||||||
rewriteTargetList(Query *parsetree, Relation target_relation)
|
rewriteTargetList(Query *parsetree, Relation target_relation,
|
||||||
|
List **attrno_list)
|
||||||
{
|
{
|
||||||
CmdType commandType = parsetree->commandType;
|
CmdType commandType = parsetree->commandType;
|
||||||
TargetEntry **new_tles;
|
TargetEntry **new_tles;
|
||||||
@ -494,6 +503,9 @@ rewriteTargetList(Query *parsetree, Relation target_relation)
|
|||||||
numattrs;
|
numattrs;
|
||||||
ListCell *temp;
|
ListCell *temp;
|
||||||
|
|
||||||
|
if (attrno_list) /* initialize optional result list */
|
||||||
|
*attrno_list = NIL;
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* We process the normal (non-junk) attributes by scanning the input tlist
|
* We process the normal (non-junk) attributes by scanning the input tlist
|
||||||
* once and transferring TLEs into an array, then scanning the array to
|
* once and transferring TLEs into an array, then scanning the array to
|
||||||
@ -519,6 +531,10 @@ rewriteTargetList(Query *parsetree, Relation target_relation)
|
|||||||
elog(ERROR, "bogus resno %d in targetlist", attrno);
|
elog(ERROR, "bogus resno %d in targetlist", attrno);
|
||||||
att_tup = target_relation->rd_att->attrs[attrno - 1];
|
att_tup = target_relation->rd_att->attrs[attrno - 1];
|
||||||
|
|
||||||
|
/* put attrno into attrno_list even if it's dropped */
|
||||||
|
if (attrno_list)
|
||||||
|
*attrno_list = lappend_int(*attrno_list, attrno);
|
||||||
|
|
||||||
/* We can (and must) ignore deleted attributes */
|
/* We can (and must) ignore deleted attributes */
|
||||||
if (att_tup->attisdropped)
|
if (att_tup->attisdropped)
|
||||||
continue;
|
continue;
|
||||||
@ -820,7 +836,7 @@ build_column_default(Relation rel, int attrno)
|
|||||||
* generally be true already, but there seem to be some corner cases
|
* generally be true already, but there seem to be some corner cases
|
||||||
* involving domain defaults where it might not be true. This should match
|
* involving domain defaults where it might not be true. This should match
|
||||||
* the parser's processing of non-defaulted expressions --- see
|
* the parser's processing of non-defaulted expressions --- see
|
||||||
* updateTargetListEntry().
|
* transformAssignedExpr().
|
||||||
*/
|
*/
|
||||||
exprtype = exprType(expr);
|
exprtype = exprType(expr);
|
||||||
|
|
||||||
@ -843,6 +859,111 @@ build_column_default(Relation rel, int attrno)
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/* Does VALUES RTE contain any SetToDefault items? */
|
||||||
|
static bool
|
||||||
|
searchForDefault(RangeTblEntry *rte)
|
||||||
|
{
|
||||||
|
ListCell *lc;
|
||||||
|
|
||||||
|
foreach(lc, rte->values_lists)
|
||||||
|
{
|
||||||
|
List *sublist = (List *) lfirst(lc);
|
||||||
|
ListCell *lc2;
|
||||||
|
|
||||||
|
foreach(lc2, sublist)
|
||||||
|
{
|
||||||
|
Node *col = (Node *) lfirst(lc2);
|
||||||
|
|
||||||
|
if (IsA(col, SetToDefault))
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* When processing INSERT ... VALUES with a VALUES RTE (ie, multiple VALUES
|
||||||
|
* lists), we have to replace any DEFAULT items in the VALUES lists with
|
||||||
|
* the appropriate default expressions. The other aspects of rewriteTargetList
|
||||||
|
* need be applied only to the query's targetlist proper.
|
||||||
|
*
|
||||||
|
* Note that we currently can't support subscripted or field assignment
|
||||||
|
* in the multi-VALUES case. The targetlist will contain simple Vars
|
||||||
|
* referencing the VALUES RTE, and therefore process_matched_tle() will
|
||||||
|
* reject any such attempt with "multiple assignments to same column".
|
||||||
|
*/
|
||||||
|
static void
|
||||||
|
rewriteValuesRTE(RangeTblEntry *rte, Relation target_relation, List *attrnos)
|
||||||
|
{
|
||||||
|
List *newValues;
|
||||||
|
ListCell *lc;
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Rebuilding all the lists is a pretty expensive proposition in a big
|
||||||
|
* VALUES list, and it's a waste of time if there aren't any DEFAULT
|
||||||
|
* placeholders. So first scan to see if there are any.
|
||||||
|
*/
|
||||||
|
if (!searchForDefault(rte))
|
||||||
|
return; /* nothing to do */
|
||||||
|
|
||||||
|
/* Check list lengths (we can assume all the VALUES sublists are alike) */
|
||||||
|
Assert(list_length(attrnos) == list_length(linitial(rte->values_lists)));
|
||||||
|
|
||||||
|
newValues = NIL;
|
||||||
|
foreach(lc, rte->values_lists)
|
||||||
|
{
|
||||||
|
List *sublist = (List *) lfirst(lc);
|
||||||
|
List *newList = NIL;
|
||||||
|
ListCell *lc2;
|
||||||
|
ListCell *lc3;
|
||||||
|
|
||||||
|
forboth(lc2, sublist, lc3, attrnos)
|
||||||
|
{
|
||||||
|
Node *col = (Node *) lfirst(lc2);
|
||||||
|
int attrno = lfirst_int(lc3);
|
||||||
|
|
||||||
|
if (IsA(col, SetToDefault))
|
||||||
|
{
|
||||||
|
Form_pg_attribute att_tup;
|
||||||
|
Node *new_expr;
|
||||||
|
|
||||||
|
att_tup = target_relation->rd_att->attrs[attrno - 1];
|
||||||
|
|
||||||
|
if (!att_tup->attisdropped)
|
||||||
|
new_expr = build_column_default(target_relation, attrno);
|
||||||
|
else
|
||||||
|
new_expr = NULL; /* force a NULL if dropped */
|
||||||
|
|
||||||
|
/*
|
||||||
|
* If there is no default (ie, default is effectively NULL),
|
||||||
|
* we've got to explicitly set the column to NULL.
|
||||||
|
*/
|
||||||
|
if (!new_expr)
|
||||||
|
{
|
||||||
|
new_expr = (Node *) makeConst(att_tup->atttypid,
|
||||||
|
att_tup->attlen,
|
||||||
|
(Datum) 0,
|
||||||
|
true, /* isnull */
|
||||||
|
att_tup->attbyval);
|
||||||
|
/* this is to catch a NOT NULL domain constraint */
|
||||||
|
new_expr = coerce_to_domain(new_expr,
|
||||||
|
InvalidOid, -1,
|
||||||
|
att_tup->atttypid,
|
||||||
|
COERCE_IMPLICIT_CAST,
|
||||||
|
false,
|
||||||
|
false);
|
||||||
|
}
|
||||||
|
newList = lappend(newList, new_expr);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
newList = lappend(newList, col);
|
||||||
|
}
|
||||||
|
newValues = lappend(newValues, newList);
|
||||||
|
}
|
||||||
|
rte->values_lists = newValues;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* matchLocks -
|
* matchLocks -
|
||||||
* match the list of locks and returns the matching rules
|
* match the list of locks and returns the matching rules
|
||||||
@ -1375,8 +1496,45 @@ RewriteQuery(Query *parsetree, List *rewrite_events)
|
|||||||
* form. This will be needed by the planner anyway, and doing it now
|
* form. This will be needed by the planner anyway, and doing it now
|
||||||
* ensures that any references to NEW.field will behave sanely.
|
* ensures that any references to NEW.field will behave sanely.
|
||||||
*/
|
*/
|
||||||
if (event == CMD_INSERT || event == CMD_UPDATE)
|
if (event == CMD_UPDATE)
|
||||||
rewriteTargetList(parsetree, rt_entry_relation);
|
rewriteTargetList(parsetree, rt_entry_relation, NULL);
|
||||||
|
else if (event == CMD_INSERT)
|
||||||
|
{
|
||||||
|
RangeTblEntry *values_rte = NULL;
|
||||||
|
|
||||||
|
/*
|
||||||
|
* If it's an INSERT ... VALUES (...), (...), ...
|
||||||
|
* there will be a single RTE for the VALUES targetlists.
|
||||||
|
*/
|
||||||
|
if (list_length(parsetree->jointree->fromlist) == 1)
|
||||||
|
{
|
||||||
|
RangeTblRef *rtr = (RangeTblRef *) linitial(parsetree->jointree->fromlist);
|
||||||
|
|
||||||
|
if (IsA(rtr, RangeTblRef))
|
||||||
|
{
|
||||||
|
RangeTblEntry *rte = rt_fetch(rtr->rtindex,
|
||||||
|
parsetree->rtable);
|
||||||
|
|
||||||
|
if (rte->rtekind == RTE_VALUES)
|
||||||
|
values_rte = rte;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (values_rte)
|
||||||
|
{
|
||||||
|
List *attrnos;
|
||||||
|
|
||||||
|
/* Process the main targetlist ... */
|
||||||
|
rewriteTargetList(parsetree, rt_entry_relation, &attrnos);
|
||||||
|
/* ... and the VALUES expression lists */
|
||||||
|
rewriteValuesRTE(values_rte, rt_entry_relation, attrnos);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
/* Process just the main targetlist */
|
||||||
|
rewriteTargetList(parsetree, rt_entry_relation, NULL);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Collect and apply the appropriate rules.
|
* Collect and apply the appropriate rules.
|
||||||
|
@ -2,7 +2,7 @@
|
|||||||
* ruleutils.c - Functions to convert stored expressions/querytrees
|
* ruleutils.c - Functions to convert stored expressions/querytrees
|
||||||
* back to source text
|
* back to source text
|
||||||
*
|
*
|
||||||
* $PostgreSQL: pgsql/src/backend/utils/adt/ruleutils.c,v 1.229 2006/07/27 19:52:06 tgl Exp $
|
* $PostgreSQL: pgsql/src/backend/utils/adt/ruleutils.c,v 1.230 2006/08/02 01:59:47 joe Exp $
|
||||||
**********************************************************************/
|
**********************************************************************/
|
||||||
|
|
||||||
#include "postgres.h"
|
#include "postgres.h"
|
||||||
@ -131,6 +131,7 @@ static void make_viewdef(StringInfo buf, HeapTuple ruletup, TupleDesc rulettc,
|
|||||||
int prettyFlags);
|
int prettyFlags);
|
||||||
static void get_query_def(Query *query, StringInfo buf, List *parentnamespace,
|
static void get_query_def(Query *query, StringInfo buf, List *parentnamespace,
|
||||||
TupleDesc resultDesc, int prettyFlags, int startIndent);
|
TupleDesc resultDesc, int prettyFlags, int startIndent);
|
||||||
|
static void get_values_def(List *values_lists, deparse_context *context);
|
||||||
static void get_select_query_def(Query *query, deparse_context *context,
|
static void get_select_query_def(Query *query, deparse_context *context,
|
||||||
TupleDesc resultDesc);
|
TupleDesc resultDesc);
|
||||||
static void get_insert_query_def(Query *query, deparse_context *context);
|
static void get_insert_query_def(Query *query, deparse_context *context);
|
||||||
@ -172,7 +173,8 @@ static void get_from_clause_coldeflist(List *names, List *types, List *typmods,
|
|||||||
deparse_context *context);
|
deparse_context *context);
|
||||||
static void get_opclass_name(Oid opclass, Oid actual_datatype,
|
static void get_opclass_name(Oid opclass, Oid actual_datatype,
|
||||||
StringInfo buf);
|
StringInfo buf);
|
||||||
static Node *processIndirection(Node *node, deparse_context *context);
|
static Node *processIndirection(Node *node, deparse_context *context,
|
||||||
|
bool printit);
|
||||||
static void printSubscripts(ArrayRef *aref, deparse_context *context);
|
static void printSubscripts(ArrayRef *aref, deparse_context *context);
|
||||||
static char *generate_relation_name(Oid relid);
|
static char *generate_relation_name(Oid relid);
|
||||||
static char *generate_function_name(Oid funcid, int nargs, Oid *argtypes);
|
static char *generate_function_name(Oid funcid, int nargs, Oid *argtypes);
|
||||||
@ -1800,6 +1802,50 @@ get_query_def(Query *query, StringInfo buf, List *parentnamespace,
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* ----------
|
||||||
|
* get_values_def - Parse back a VALUES list
|
||||||
|
* ----------
|
||||||
|
*/
|
||||||
|
static void
|
||||||
|
get_values_def(List *values_lists, deparse_context *context)
|
||||||
|
{
|
||||||
|
StringInfo buf = context->buf;
|
||||||
|
bool first_list = true;
|
||||||
|
ListCell *vtl;
|
||||||
|
|
||||||
|
appendStringInfoString(buf, "VALUES ");
|
||||||
|
|
||||||
|
foreach(vtl, values_lists)
|
||||||
|
{
|
||||||
|
List *sublist = (List *) lfirst(vtl);
|
||||||
|
bool first_col = true;
|
||||||
|
ListCell *lc;
|
||||||
|
|
||||||
|
if (first_list)
|
||||||
|
first_list = false;
|
||||||
|
else
|
||||||
|
appendStringInfoString(buf, ", ");
|
||||||
|
|
||||||
|
appendStringInfoChar(buf, '(');
|
||||||
|
foreach(lc, sublist)
|
||||||
|
{
|
||||||
|
Node *col = (Node *) lfirst(lc);
|
||||||
|
|
||||||
|
if (first_col)
|
||||||
|
first_col = false;
|
||||||
|
else
|
||||||
|
appendStringInfoChar(buf, ',');
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Strip any top-level nodes representing indirection assignments,
|
||||||
|
* then print the result.
|
||||||
|
*/
|
||||||
|
get_rule_expr(processIndirection(col, context, false),
|
||||||
|
context, false);
|
||||||
|
}
|
||||||
|
appendStringInfoChar(buf, ')');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/* ----------
|
/* ----------
|
||||||
* get_select_query_def - Parse back a SELECT parsetree
|
* get_select_query_def - Parse back a SELECT parsetree
|
||||||
@ -1910,14 +1956,37 @@ get_basic_select_query(Query *query, deparse_context *context,
|
|||||||
ListCell *l;
|
ListCell *l;
|
||||||
int colno;
|
int colno;
|
||||||
|
|
||||||
/*
|
|
||||||
* Build up the query string - first we say SELECT
|
|
||||||
*/
|
|
||||||
if (PRETTY_INDENT(context))
|
if (PRETTY_INDENT(context))
|
||||||
{
|
{
|
||||||
context->indentLevel += PRETTYINDENT_STD;
|
context->indentLevel += PRETTYINDENT_STD;
|
||||||
appendStringInfoChar(buf, ' ');
|
appendStringInfoChar(buf, ' ');
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* If the query looks like SELECT * FROM (VALUES ...), then print just
|
||||||
|
* the VALUES part. This reverses what transformValuesClause() did at
|
||||||
|
* parse time. If the jointree contains just a single VALUES RTE,
|
||||||
|
* we assume this case applies (without looking at the targetlist...)
|
||||||
|
*/
|
||||||
|
if (list_length(query->jointree->fromlist) == 1)
|
||||||
|
{
|
||||||
|
RangeTblRef *rtr = (RangeTblRef *) linitial(query->jointree->fromlist);
|
||||||
|
|
||||||
|
if (IsA(rtr, RangeTblRef))
|
||||||
|
{
|
||||||
|
RangeTblEntry *rte = rt_fetch(rtr->rtindex, query->rtable);
|
||||||
|
|
||||||
|
if (rte->rtekind == RTE_VALUES)
|
||||||
|
{
|
||||||
|
get_values_def(rte->values_lists, context);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Build up the query string - first we say SELECT
|
||||||
|
*/
|
||||||
appendStringInfo(buf, "SELECT");
|
appendStringInfo(buf, "SELECT");
|
||||||
|
|
||||||
/* Add the DISTINCT clause if given */
|
/* Add the DISTINCT clause if given */
|
||||||
@ -2191,24 +2260,37 @@ get_insert_query_def(Query *query, deparse_context *context)
|
|||||||
{
|
{
|
||||||
StringInfo buf = context->buf;
|
StringInfo buf = context->buf;
|
||||||
RangeTblEntry *select_rte = NULL;
|
RangeTblEntry *select_rte = NULL;
|
||||||
|
RangeTblEntry *values_rte = NULL;
|
||||||
RangeTblEntry *rte;
|
RangeTblEntry *rte;
|
||||||
char *sep;
|
char *sep;
|
||||||
|
ListCell *values_cell;
|
||||||
ListCell *l;
|
ListCell *l;
|
||||||
List *strippedexprs;
|
List *strippedexprs;
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* If it's an INSERT ... SELECT there will be a single subquery RTE for
|
* If it's an INSERT ... SELECT or VALUES (...), (...), ...
|
||||||
* the SELECT.
|
* there will be a single RTE for the SELECT or VALUES.
|
||||||
*/
|
*/
|
||||||
foreach(l, query->rtable)
|
foreach(l, query->rtable)
|
||||||
{
|
{
|
||||||
rte = (RangeTblEntry *) lfirst(l);
|
rte = (RangeTblEntry *) lfirst(l);
|
||||||
if (rte->rtekind != RTE_SUBQUERY)
|
|
||||||
continue;
|
if (rte->rtekind == RTE_SUBQUERY)
|
||||||
if (select_rte)
|
{
|
||||||
elog(ERROR, "too many RTEs in INSERT");
|
if (select_rte)
|
||||||
select_rte = rte;
|
elog(ERROR, "too many subquery RTEs in INSERT");
|
||||||
|
select_rte = rte;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (rte->rtekind == RTE_VALUES)
|
||||||
|
{
|
||||||
|
if (values_rte)
|
||||||
|
elog(ERROR, "too many values RTEs in INSERT");
|
||||||
|
values_rte = rte;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
if (select_rte && values_rte)
|
||||||
|
elog(ERROR, "both subquery and values RTEs in INSERT");
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Start the query with INSERT INTO relname
|
* Start the query with INSERT INTO relname
|
||||||
@ -2225,9 +2307,17 @@ get_insert_query_def(Query *query, deparse_context *context)
|
|||||||
generate_relation_name(rte->relid));
|
generate_relation_name(rte->relid));
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Add the insert-column-names list, and make a list of the actual
|
* Add the insert-column-names list. To handle indirection properly,
|
||||||
* assignment source expressions.
|
* we need to look for indirection nodes in the top targetlist (if it's
|
||||||
|
* INSERT ... SELECT or INSERT ... single VALUES), or in the first
|
||||||
|
* expression list of the VALUES RTE (if it's INSERT ... multi VALUES).
|
||||||
|
* We assume that all the expression lists will have similar indirection
|
||||||
|
* in the latter case.
|
||||||
*/
|
*/
|
||||||
|
if (values_rte)
|
||||||
|
values_cell = list_head((List *) linitial(values_rte->values_lists));
|
||||||
|
else
|
||||||
|
values_cell = NULL;
|
||||||
strippedexprs = NIL;
|
strippedexprs = NIL;
|
||||||
sep = "";
|
sep = "";
|
||||||
foreach(l, query->targetList)
|
foreach(l, query->targetList)
|
||||||
@ -2252,23 +2342,41 @@ get_insert_query_def(Query *query, deparse_context *context)
|
|||||||
* Print any indirection needed (subfields or subscripts), and strip
|
* Print any indirection needed (subfields or subscripts), and strip
|
||||||
* off the top-level nodes representing the indirection assignments.
|
* off the top-level nodes representing the indirection assignments.
|
||||||
*/
|
*/
|
||||||
strippedexprs = lappend(strippedexprs,
|
if (values_cell)
|
||||||
processIndirection((Node *) tle->expr,
|
{
|
||||||
context));
|
/* we discard the stripped expression in this case */
|
||||||
|
processIndirection((Node *) lfirst(values_cell), context, true);
|
||||||
|
values_cell = lnext(values_cell);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
/* we keep a list of the stripped expressions in this case */
|
||||||
|
strippedexprs = lappend(strippedexprs,
|
||||||
|
processIndirection((Node *) tle->expr,
|
||||||
|
context, true));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
appendStringInfo(buf, ") ");
|
appendStringInfo(buf, ") ");
|
||||||
|
|
||||||
/* Add the VALUES or the SELECT */
|
if (select_rte)
|
||||||
if (select_rte == NULL)
|
|
||||||
{
|
{
|
||||||
|
/* Add the SELECT */
|
||||||
|
get_query_def(select_rte->subquery, buf, NIL, NULL,
|
||||||
|
context->prettyFlags, context->indentLevel);
|
||||||
|
}
|
||||||
|
else if (values_rte)
|
||||||
|
{
|
||||||
|
/* Add the multi-VALUES expression lists */
|
||||||
|
get_values_def(values_rte->values_lists, context);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
/* Add the single-VALUES expression list */
|
||||||
appendContextKeyword(context, "VALUES (",
|
appendContextKeyword(context, "VALUES (",
|
||||||
-PRETTYINDENT_STD, PRETTYINDENT_STD, 2);
|
-PRETTYINDENT_STD, PRETTYINDENT_STD, 2);
|
||||||
get_rule_expr((Node *) strippedexprs, context, false);
|
get_rule_expr((Node *) strippedexprs, context, false);
|
||||||
appendStringInfoChar(buf, ')');
|
appendStringInfoChar(buf, ')');
|
||||||
}
|
}
|
||||||
else
|
|
||||||
get_query_def(select_rte->subquery, buf, NIL, NULL,
|
|
||||||
context->prettyFlags, context->indentLevel);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@ -2323,7 +2431,7 @@ get_update_query_def(Query *query, deparse_context *context)
|
|||||||
* Print any indirection needed (subfields or subscripts), and strip
|
* Print any indirection needed (subfields or subscripts), and strip
|
||||||
* off the top-level nodes representing the indirection assignments.
|
* off the top-level nodes representing the indirection assignments.
|
||||||
*/
|
*/
|
||||||
expr = processIndirection((Node *) tle->expr, context);
|
expr = processIndirection((Node *) tle->expr, context, true);
|
||||||
|
|
||||||
appendStringInfo(buf, " = ");
|
appendStringInfo(buf, " = ");
|
||||||
|
|
||||||
@ -2612,11 +2720,12 @@ get_name_for_var_field(Var *var, int fieldno,
|
|||||||
switch (rte->rtekind)
|
switch (rte->rtekind)
|
||||||
{
|
{
|
||||||
case RTE_RELATION:
|
case RTE_RELATION:
|
||||||
|
case RTE_VALUES:
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* This case should not occur: a column of a table shouldn't have
|
* This case should not occur: a column of a table or values list
|
||||||
* type RECORD. Fall through and fail (most likely) at the
|
* shouldn't have type RECORD. Fall through and fail
|
||||||
* bottom.
|
* (most likely) at the bottom.
|
||||||
*/
|
*/
|
||||||
break;
|
break;
|
||||||
case RTE_SUBQUERY:
|
case RTE_SUBQUERY:
|
||||||
@ -4232,6 +4341,10 @@ get_from_clause_item(Node *jtnode, Query *query, deparse_context *context)
|
|||||||
/* Function RTE */
|
/* Function RTE */
|
||||||
get_rule_expr(rte->funcexpr, context, true);
|
get_rule_expr(rte->funcexpr, context, true);
|
||||||
break;
|
break;
|
||||||
|
case RTE_VALUES:
|
||||||
|
/* Values list RTE */
|
||||||
|
get_values_def(rte->values_lists, context);
|
||||||
|
break;
|
||||||
default:
|
default:
|
||||||
elog(ERROR, "unrecognized RTE kind: %d", (int) rte->rtekind);
|
elog(ERROR, "unrecognized RTE kind: %d", (int) rte->rtekind);
|
||||||
break;
|
break;
|
||||||
@ -4576,12 +4689,12 @@ get_opclass_name(Oid opclass, Oid actual_datatype,
|
|||||||
* processIndirection - take care of array and subfield assignment
|
* processIndirection - take care of array and subfield assignment
|
||||||
*
|
*
|
||||||
* We strip any top-level FieldStore or assignment ArrayRef nodes that
|
* We strip any top-level FieldStore or assignment ArrayRef nodes that
|
||||||
* appear in the input, printing out the appropriate decoration for the
|
* appear in the input, and return the subexpression that's to be assigned.
|
||||||
* base column name (that the caller just printed). We return the
|
* If printit is true, we also print out the appropriate decoration for the
|
||||||
* subexpression that's to be assigned.
|
* base column name (that the caller just printed).
|
||||||
*/
|
*/
|
||||||
static Node *
|
static Node *
|
||||||
processIndirection(Node *node, deparse_context *context)
|
processIndirection(Node *node, deparse_context *context, bool printit)
|
||||||
{
|
{
|
||||||
StringInfo buf = context->buf;
|
StringInfo buf = context->buf;
|
||||||
|
|
||||||
@ -4602,15 +4715,16 @@ processIndirection(Node *node, deparse_context *context)
|
|||||||
format_type_be(fstore->resulttype));
|
format_type_be(fstore->resulttype));
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Get the field name. Note we assume here that there's only one
|
* Print the field name. Note we assume here that there's only
|
||||||
* field being assigned to. This is okay in stored rules but
|
* one field being assigned to. This is okay in stored rules but
|
||||||
* could be wrong in executable target lists. Presently no
|
* could be wrong in executable target lists. Presently no
|
||||||
* problem since explain.c doesn't print plan targetlists, but
|
* problem since explain.c doesn't print plan targetlists, but
|
||||||
* someday may have to think of something ...
|
* someday may have to think of something ...
|
||||||
*/
|
*/
|
||||||
fieldname = get_relid_attribute_name(typrelid,
|
fieldname = get_relid_attribute_name(typrelid,
|
||||||
linitial_int(fstore->fieldnums));
|
linitial_int(fstore->fieldnums));
|
||||||
appendStringInfo(buf, ".%s", quote_identifier(fieldname));
|
if (printit)
|
||||||
|
appendStringInfo(buf, ".%s", quote_identifier(fieldname));
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* We ignore arg since it should be an uninteresting reference to
|
* We ignore arg since it should be an uninteresting reference to
|
||||||
@ -4624,7 +4738,8 @@ processIndirection(Node *node, deparse_context *context)
|
|||||||
|
|
||||||
if (aref->refassgnexpr == NULL)
|
if (aref->refassgnexpr == NULL)
|
||||||
break;
|
break;
|
||||||
printSubscripts(aref, context);
|
if (printit)
|
||||||
|
printSubscripts(aref, context);
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* We ignore refexpr since it should be an uninteresting reference
|
* We ignore refexpr since it should be an uninteresting reference
|
||||||
|
@ -37,7 +37,7 @@
|
|||||||
* Portions Copyright (c) 1996-2006, PostgreSQL Global Development Group
|
* Portions Copyright (c) 1996-2006, PostgreSQL Global Development Group
|
||||||
* Portions Copyright (c) 1994, Regents of the University of California
|
* Portions Copyright (c) 1994, Regents of the University of California
|
||||||
*
|
*
|
||||||
* $PostgreSQL: pgsql/src/include/catalog/catversion.h,v 1.345 2006/07/31 20:09:05 tgl Exp $
|
* $PostgreSQL: pgsql/src/include/catalog/catversion.h,v 1.346 2006/08/02 01:59:47 joe Exp $
|
||||||
*
|
*
|
||||||
*-------------------------------------------------------------------------
|
*-------------------------------------------------------------------------
|
||||||
*/
|
*/
|
||||||
@ -53,6 +53,6 @@
|
|||||||
*/
|
*/
|
||||||
|
|
||||||
/* yyyymmddN */
|
/* yyyymmddN */
|
||||||
#define CATALOG_VERSION_NO 200607311
|
#define CATALOG_VERSION_NO 200608011
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
27
src/include/executor/nodeValuesscan.h
Normal file
27
src/include/executor/nodeValuesscan.h
Normal file
@ -0,0 +1,27 @@
|
|||||||
|
/*-------------------------------------------------------------------------
|
||||||
|
*
|
||||||
|
* nodeValuesscan.h
|
||||||
|
*
|
||||||
|
*
|
||||||
|
*
|
||||||
|
* Portions Copyright (c) 1996-2006, PostgreSQL Global Development Group
|
||||||
|
* Portions Copyright (c) 1994, Regents of the University of California
|
||||||
|
*
|
||||||
|
* $PostgreSQL: pgsql/src/include/executor/nodeValuesscan.h,v 1.1 2006/08/02 01:59:47 joe Exp $
|
||||||
|
*
|
||||||
|
*-------------------------------------------------------------------------
|
||||||
|
*/
|
||||||
|
#ifndef NODEVALUESSCAN_H
|
||||||
|
#define NODEVALUESSCAN_H
|
||||||
|
|
||||||
|
#include "nodes/execnodes.h"
|
||||||
|
|
||||||
|
extern int ExecCountSlotsValuesScan(ValuesScan *node);
|
||||||
|
extern ValuesScanState *ExecInitValuesScan(ValuesScan *node, EState *estate, int eflags);
|
||||||
|
extern TupleTableSlot *ExecValuesScan(ValuesScanState *node);
|
||||||
|
extern void ExecEndValuesScan(ValuesScanState *node);
|
||||||
|
extern void ExecValuesMarkPos(ValuesScanState *node);
|
||||||
|
extern void ExecValuesRestrPos(ValuesScanState *node);
|
||||||
|
extern void ExecValuesReScan(ValuesScanState *node, ExprContext *exprCtxt);
|
||||||
|
|
||||||
|
#endif /* NODEVALUESSCAN_H */
|
@ -7,7 +7,7 @@
|
|||||||
* Portions Copyright (c) 1996-2006, PostgreSQL Global Development Group
|
* Portions Copyright (c) 1996-2006, PostgreSQL Global Development Group
|
||||||
* Portions Copyright (c) 1994, Regents of the University of California
|
* Portions Copyright (c) 1994, Regents of the University of California
|
||||||
*
|
*
|
||||||
* $PostgreSQL: pgsql/src/include/nodes/execnodes.h,v 1.155 2006/07/27 19:52:07 tgl Exp $
|
* $PostgreSQL: pgsql/src/include/nodes/execnodes.h,v 1.156 2006/08/02 01:59:47 joe Exp $
|
||||||
*
|
*
|
||||||
*-------------------------------------------------------------------------
|
*-------------------------------------------------------------------------
|
||||||
*/
|
*/
|
||||||
@ -1041,6 +1041,27 @@ typedef struct FunctionScanState
|
|||||||
ExprState *funcexpr;
|
ExprState *funcexpr;
|
||||||
} FunctionScanState;
|
} FunctionScanState;
|
||||||
|
|
||||||
|
/* ----------------
|
||||||
|
* ValuesScanState information
|
||||||
|
*
|
||||||
|
* Values nodes are used to scan the results of a
|
||||||
|
* values list appearing in FROM or INSERT
|
||||||
|
*
|
||||||
|
* exprlists array of expression lists being evaluated
|
||||||
|
* array_len size of array
|
||||||
|
* curr_idx current array index (0-based)
|
||||||
|
* marked_idx marked position (for mark/restore)
|
||||||
|
* ----------------
|
||||||
|
*/
|
||||||
|
typedef struct ValuesScanState
|
||||||
|
{
|
||||||
|
ScanState ss; /* its first field is NodeTag */
|
||||||
|
List **exprlists;
|
||||||
|
int array_len;
|
||||||
|
int curr_idx;
|
||||||
|
int marked_idx;
|
||||||
|
} ValuesScanState;
|
||||||
|
|
||||||
/* ----------------------------------------------------------------
|
/* ----------------------------------------------------------------
|
||||||
* Join State Information
|
* Join State Information
|
||||||
* ----------------------------------------------------------------
|
* ----------------------------------------------------------------
|
||||||
|
@ -7,7 +7,7 @@
|
|||||||
* Portions Copyright (c) 1996-2006, PostgreSQL Global Development Group
|
* Portions Copyright (c) 1996-2006, PostgreSQL Global Development Group
|
||||||
* Portions Copyright (c) 1994, Regents of the University of California
|
* Portions Copyright (c) 1994, Regents of the University of California
|
||||||
*
|
*
|
||||||
* $PostgreSQL: pgsql/src/include/nodes/nodes.h,v 1.186 2006/04/30 18:30:40 tgl Exp $
|
* $PostgreSQL: pgsql/src/include/nodes/nodes.h,v 1.187 2006/08/02 01:59:47 joe Exp $
|
||||||
*
|
*
|
||||||
*-------------------------------------------------------------------------
|
*-------------------------------------------------------------------------
|
||||||
*/
|
*/
|
||||||
@ -54,6 +54,7 @@ typedef enum NodeTag
|
|||||||
T_TidScan,
|
T_TidScan,
|
||||||
T_SubqueryScan,
|
T_SubqueryScan,
|
||||||
T_FunctionScan,
|
T_FunctionScan,
|
||||||
|
T_ValuesScan,
|
||||||
T_Join,
|
T_Join,
|
||||||
T_NestLoop,
|
T_NestLoop,
|
||||||
T_MergeJoin,
|
T_MergeJoin,
|
||||||
@ -85,6 +86,7 @@ typedef enum NodeTag
|
|||||||
T_TidScanState,
|
T_TidScanState,
|
||||||
T_SubqueryScanState,
|
T_SubqueryScanState,
|
||||||
T_FunctionScanState,
|
T_FunctionScanState,
|
||||||
|
T_ValuesScanState,
|
||||||
T_JoinState,
|
T_JoinState,
|
||||||
T_NestLoopState,
|
T_NestLoopState,
|
||||||
T_MergeJoinState,
|
T_MergeJoinState,
|
||||||
|
@ -7,7 +7,7 @@
|
|||||||
* Portions Copyright (c) 1996-2006, PostgreSQL Global Development Group
|
* Portions Copyright (c) 1996-2006, PostgreSQL Global Development Group
|
||||||
* Portions Copyright (c) 1994, Regents of the University of California
|
* Portions Copyright (c) 1994, Regents of the University of California
|
||||||
*
|
*
|
||||||
* $PostgreSQL: pgsql/src/include/nodes/parsenodes.h,v 1.319 2006/07/31 01:16:38 tgl Exp $
|
* $PostgreSQL: pgsql/src/include/nodes/parsenodes.h,v 1.320 2006/08/02 01:59:47 joe Exp $
|
||||||
*
|
*
|
||||||
*-------------------------------------------------------------------------
|
*-------------------------------------------------------------------------
|
||||||
*/
|
*/
|
||||||
@ -303,13 +303,13 @@ typedef struct A_Indirection
|
|||||||
* ResTarget -
|
* ResTarget -
|
||||||
* result target (used in target list of pre-transformed parse trees)
|
* result target (used in target list of pre-transformed parse trees)
|
||||||
*
|
*
|
||||||
* In a SELECT or INSERT target list, 'name' is the column label from an
|
* In a SELECT target list, 'name' is the column label from an
|
||||||
* 'AS ColumnLabel' clause, or NULL if there was none, and 'val' is the
|
* 'AS ColumnLabel' clause, or NULL if there was none, and 'val' is the
|
||||||
* value expression itself. The 'indirection' field is not used.
|
* value expression itself. The 'indirection' field is not used.
|
||||||
*
|
*
|
||||||
* INSERT has a second ResTarget list which is the target-column-names list.
|
* INSERT uses ResTarget in its target-column-names list. Here, 'name' is
|
||||||
* Here, 'val' is not used, 'name' is the name of the destination column,
|
* the name of the destination column, 'indirection' stores any subscripts
|
||||||
* and 'indirection' stores any subscripts attached to the destination.
|
* attached to the destination, and 'val' is not used.
|
||||||
*
|
*
|
||||||
* In an UPDATE target list, 'name' is the name of the destination column,
|
* In an UPDATE target list, 'name' is the name of the destination column,
|
||||||
* 'indirection' stores any subscripts attached to the destination, and
|
* 'indirection' stores any subscripts attached to the destination, and
|
||||||
@ -517,7 +517,8 @@ typedef enum RTEKind
|
|||||||
RTE_SUBQUERY, /* subquery in FROM */
|
RTE_SUBQUERY, /* subquery in FROM */
|
||||||
RTE_JOIN, /* join */
|
RTE_JOIN, /* join */
|
||||||
RTE_SPECIAL, /* special rule relation (NEW or OLD) */
|
RTE_SPECIAL, /* special rule relation (NEW or OLD) */
|
||||||
RTE_FUNCTION /* function in FROM */
|
RTE_FUNCTION, /* function in FROM */
|
||||||
|
RTE_VALUES /* VALUES (<exprlist>), (<exprlist>), ... */
|
||||||
} RTEKind;
|
} RTEKind;
|
||||||
|
|
||||||
typedef struct RangeTblEntry
|
typedef struct RangeTblEntry
|
||||||
@ -553,6 +554,11 @@ typedef struct RangeTblEntry
|
|||||||
List *funccoltypes; /* OID list of column type OIDs */
|
List *funccoltypes; /* OID list of column type OIDs */
|
||||||
List *funccoltypmods; /* integer list of column typmods */
|
List *funccoltypmods; /* integer list of column typmods */
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Fields valid for a values RTE (else NIL):
|
||||||
|
*/
|
||||||
|
List *values_lists; /* list of expression lists */
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Fields valid for a join RTE (else NULL/zero):
|
* Fields valid for a join RTE (else NULL/zero):
|
||||||
*
|
*
|
||||||
@ -630,6 +636,10 @@ typedef struct RowMarkClause
|
|||||||
|
|
||||||
/* ----------------------
|
/* ----------------------
|
||||||
* Insert Statement
|
* Insert Statement
|
||||||
|
*
|
||||||
|
* The source expression is represented by SelectStmt for both the
|
||||||
|
* SELECT and VALUES cases. If selectStmt is NULL, then the query
|
||||||
|
* is INSERT ... DEFAULT VALUES.
|
||||||
* ----------------------
|
* ----------------------
|
||||||
*/
|
*/
|
||||||
typedef struct InsertStmt
|
typedef struct InsertStmt
|
||||||
@ -637,14 +647,7 @@ typedef struct InsertStmt
|
|||||||
NodeTag type;
|
NodeTag type;
|
||||||
RangeVar *relation; /* relation to insert into */
|
RangeVar *relation; /* relation to insert into */
|
||||||
List *cols; /* optional: names of the target columns */
|
List *cols; /* optional: names of the target columns */
|
||||||
|
Node *selectStmt; /* the source SELECT/VALUES, or NULL */
|
||||||
/*
|
|
||||||
* An INSERT statement has *either* VALUES or SELECT, never both. If
|
|
||||||
* VALUES, a targetList is supplied (empty for DEFAULT VALUES). If SELECT,
|
|
||||||
* a complete SelectStmt (or set-operation tree) is supplied.
|
|
||||||
*/
|
|
||||||
List *targetList; /* the target list (of ResTarget) */
|
|
||||||
Node *selectStmt; /* the source SELECT */
|
|
||||||
} InsertStmt;
|
} InsertStmt;
|
||||||
|
|
||||||
/* ----------------------
|
/* ----------------------
|
||||||
@ -676,9 +679,9 @@ typedef struct UpdateStmt
|
|||||||
* Select Statement
|
* Select Statement
|
||||||
*
|
*
|
||||||
* A "simple" SELECT is represented in the output of gram.y by a single
|
* A "simple" SELECT is represented in the output of gram.y by a single
|
||||||
* SelectStmt node. A SELECT construct containing set operators (UNION,
|
* SelectStmt node; so is a VALUES construct. A query containing set
|
||||||
* INTERSECT, EXCEPT) is represented by a tree of SelectStmt nodes, in
|
* operators (UNION, INTERSECT, EXCEPT) is represented by a tree of SelectStmt
|
||||||
* which the leaf nodes are component SELECTs and the internal nodes
|
* nodes, in which the leaf nodes are component SELECTs and the internal nodes
|
||||||
* represent UNION, INTERSECT, or EXCEPT operators. Using the same node
|
* represent UNION, INTERSECT, or EXCEPT operators. Using the same node
|
||||||
* type for both leaf and internal nodes allows gram.y to stick ORDER BY,
|
* type for both leaf and internal nodes allows gram.y to stick ORDER BY,
|
||||||
* LIMIT, etc, clause values into a SELECT statement without worrying
|
* LIMIT, etc, clause values into a SELECT statement without worrying
|
||||||
@ -716,6 +719,16 @@ typedef struct SelectStmt
|
|||||||
List *groupClause; /* GROUP BY clauses */
|
List *groupClause; /* GROUP BY clauses */
|
||||||
Node *havingClause; /* HAVING conditional-expression */
|
Node *havingClause; /* HAVING conditional-expression */
|
||||||
|
|
||||||
|
/*
|
||||||
|
* In a "leaf" node representing a VALUES list, the above fields are all
|
||||||
|
* null, and instead this field is set. Note that the elements of
|
||||||
|
* the sublists are just expressions, without ResTarget decoration.
|
||||||
|
* Also note that a list element can be DEFAULT (represented as a
|
||||||
|
* SetToDefault node), regardless of the context of the VALUES list.
|
||||||
|
* It's up to parse analysis to reject that where not valid.
|
||||||
|
*/
|
||||||
|
List *valuesLists; /* untransformed list of expression lists */
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* These fields are used in both "leaf" SelectStmts and upper-level
|
* These fields are used in both "leaf" SelectStmts and upper-level
|
||||||
* SelectStmts.
|
* SelectStmts.
|
||||||
|
@ -7,7 +7,7 @@
|
|||||||
* Portions Copyright (c) 1996-2006, PostgreSQL Global Development Group
|
* Portions Copyright (c) 1996-2006, PostgreSQL Global Development Group
|
||||||
* Portions Copyright (c) 1994, Regents of the University of California
|
* Portions Copyright (c) 1994, Regents of the University of California
|
||||||
*
|
*
|
||||||
* $PostgreSQL: pgsql/src/include/nodes/plannodes.h,v 1.84 2006/07/26 19:31:51 tgl Exp $
|
* $PostgreSQL: pgsql/src/include/nodes/plannodes.h,v 1.85 2006/08/02 01:59:47 joe Exp $
|
||||||
*
|
*
|
||||||
*-------------------------------------------------------------------------
|
*-------------------------------------------------------------------------
|
||||||
*/
|
*/
|
||||||
@ -296,6 +296,16 @@ typedef struct FunctionScan
|
|||||||
/* no other fields needed at present */
|
/* no other fields needed at present */
|
||||||
} FunctionScan;
|
} FunctionScan;
|
||||||
|
|
||||||
|
/* ----------------
|
||||||
|
* ValuesScan node
|
||||||
|
* ----------------
|
||||||
|
*/
|
||||||
|
typedef struct ValuesScan
|
||||||
|
{
|
||||||
|
Scan scan;
|
||||||
|
/* no other fields needed at present */
|
||||||
|
} ValuesScan;
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* ==========
|
* ==========
|
||||||
* Join nodes
|
* Join nodes
|
||||||
|
@ -7,7 +7,7 @@
|
|||||||
* Portions Copyright (c) 1996-2006, PostgreSQL Global Development Group
|
* Portions Copyright (c) 1996-2006, PostgreSQL Global Development Group
|
||||||
* Portions Copyright (c) 1994, Regents of the University of California
|
* Portions Copyright (c) 1994, Regents of the University of California
|
||||||
*
|
*
|
||||||
* $PostgreSQL: pgsql/src/include/optimizer/cost.h,v 1.78 2006/07/26 11:35:56 petere Exp $
|
* $PostgreSQL: pgsql/src/include/optimizer/cost.h,v 1.79 2006/08/02 01:59:48 joe Exp $
|
||||||
*
|
*
|
||||||
*-------------------------------------------------------------------------
|
*-------------------------------------------------------------------------
|
||||||
*/
|
*/
|
||||||
@ -70,6 +70,8 @@ extern void cost_tidscan(Path *path, PlannerInfo *root,
|
|||||||
extern void cost_subqueryscan(Path *path, RelOptInfo *baserel);
|
extern void cost_subqueryscan(Path *path, RelOptInfo *baserel);
|
||||||
extern void cost_functionscan(Path *path, PlannerInfo *root,
|
extern void cost_functionscan(Path *path, PlannerInfo *root,
|
||||||
RelOptInfo *baserel);
|
RelOptInfo *baserel);
|
||||||
|
extern void cost_valuesscan(Path *path, PlannerInfo *root,
|
||||||
|
RelOptInfo *baserel);
|
||||||
extern void cost_sort(Path *path, PlannerInfo *root,
|
extern void cost_sort(Path *path, PlannerInfo *root,
|
||||||
List *pathkeys, Cost input_cost, double tuples, int width);
|
List *pathkeys, Cost input_cost, double tuples, int width);
|
||||||
extern void cost_material(Path *path,
|
extern void cost_material(Path *path,
|
||||||
@ -94,6 +96,7 @@ extern void set_joinrel_size_estimates(PlannerInfo *root, RelOptInfo *rel,
|
|||||||
JoinType jointype,
|
JoinType jointype,
|
||||||
List *restrictlist);
|
List *restrictlist);
|
||||||
extern void set_function_size_estimates(PlannerInfo *root, RelOptInfo *rel);
|
extern void set_function_size_estimates(PlannerInfo *root, RelOptInfo *rel);
|
||||||
|
extern void set_values_size_estimates(PlannerInfo *root, RelOptInfo *rel);
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* prototypes for clausesel.c
|
* prototypes for clausesel.c
|
||||||
|
@ -7,7 +7,7 @@
|
|||||||
* Portions Copyright (c) 1996-2006, PostgreSQL Global Development Group
|
* Portions Copyright (c) 1996-2006, PostgreSQL Global Development Group
|
||||||
* Portions Copyright (c) 1994, Regents of the University of California
|
* Portions Copyright (c) 1994, Regents of the University of California
|
||||||
*
|
*
|
||||||
* $PostgreSQL: pgsql/src/include/optimizer/pathnode.h,v 1.70 2006/07/22 15:41:56 tgl Exp $
|
* $PostgreSQL: pgsql/src/include/optimizer/pathnode.h,v 1.71 2006/08/02 01:59:48 joe Exp $
|
||||||
*
|
*
|
||||||
*-------------------------------------------------------------------------
|
*-------------------------------------------------------------------------
|
||||||
*/
|
*/
|
||||||
@ -53,6 +53,7 @@ extern UniquePath *create_unique_path(PlannerInfo *root, RelOptInfo *rel,
|
|||||||
Path *subpath);
|
Path *subpath);
|
||||||
extern Path *create_subqueryscan_path(RelOptInfo *rel, List *pathkeys);
|
extern Path *create_subqueryscan_path(RelOptInfo *rel, List *pathkeys);
|
||||||
extern Path *create_functionscan_path(PlannerInfo *root, RelOptInfo *rel);
|
extern Path *create_functionscan_path(PlannerInfo *root, RelOptInfo *rel);
|
||||||
|
extern Path *create_valuesscan_path(PlannerInfo *root, RelOptInfo *rel);
|
||||||
|
|
||||||
extern NestPath *create_nestloop_path(PlannerInfo *root,
|
extern NestPath *create_nestloop_path(PlannerInfo *root,
|
||||||
RelOptInfo *joinrel,
|
RelOptInfo *joinrel,
|
||||||
|
@ -7,7 +7,7 @@
|
|||||||
* Portions Copyright (c) 1996-2006, PostgreSQL Global Development Group
|
* Portions Copyright (c) 1996-2006, PostgreSQL Global Development Group
|
||||||
* Portions Copyright (c) 1994, Regents of the University of California
|
* Portions Copyright (c) 1994, Regents of the University of California
|
||||||
*
|
*
|
||||||
* $PostgreSQL: pgsql/src/include/parser/parse_relation.h,v 1.53 2006/03/14 22:48:22 tgl Exp $
|
* $PostgreSQL: pgsql/src/include/parser/parse_relation.h,v 1.54 2006/08/02 01:59:48 joe Exp $
|
||||||
*
|
*
|
||||||
*-------------------------------------------------------------------------
|
*-------------------------------------------------------------------------
|
||||||
*/
|
*/
|
||||||
@ -59,6 +59,10 @@ extern RangeTblEntry *addRangeTableEntryForFunction(ParseState *pstate,
|
|||||||
Node *funcexpr,
|
Node *funcexpr,
|
||||||
RangeFunction *rangefunc,
|
RangeFunction *rangefunc,
|
||||||
bool inFromCl);
|
bool inFromCl);
|
||||||
|
extern RangeTblEntry *addRangeTableEntryForValues(ParseState *pstate,
|
||||||
|
List *exprs,
|
||||||
|
Alias *alias,
|
||||||
|
bool inFromCl);
|
||||||
extern RangeTblEntry *addRangeTableEntryForJoin(ParseState *pstate,
|
extern RangeTblEntry *addRangeTableEntryForJoin(ParseState *pstate,
|
||||||
List *colnames,
|
List *colnames,
|
||||||
JoinType jointype,
|
JoinType jointype,
|
||||||
|
@ -7,7 +7,7 @@
|
|||||||
* Portions Copyright (c) 1996-2006, PostgreSQL Global Development Group
|
* Portions Copyright (c) 1996-2006, PostgreSQL Global Development Group
|
||||||
* Portions Copyright (c) 1994, Regents of the University of California
|
* Portions Copyright (c) 1994, Regents of the University of California
|
||||||
*
|
*
|
||||||
* $PostgreSQL: pgsql/src/include/parser/parse_target.h,v 1.40 2006/06/26 17:24:41 tgl Exp $
|
* $PostgreSQL: pgsql/src/include/parser/parse_target.h,v 1.41 2006/08/02 01:59:48 joe Exp $
|
||||||
*
|
*
|
||||||
*-------------------------------------------------------------------------
|
*-------------------------------------------------------------------------
|
||||||
*/
|
*/
|
||||||
@ -18,14 +18,16 @@
|
|||||||
|
|
||||||
|
|
||||||
extern List *transformTargetList(ParseState *pstate, List *targetlist);
|
extern List *transformTargetList(ParseState *pstate, List *targetlist);
|
||||||
|
extern List *transformExpressionList(ParseState *pstate, List *exprlist);
|
||||||
extern void markTargetListOrigins(ParseState *pstate, List *targetlist);
|
extern void markTargetListOrigins(ParseState *pstate, List *targetlist);
|
||||||
extern TargetEntry *transformTargetEntry(ParseState *pstate,
|
extern TargetEntry *transformTargetEntry(ParseState *pstate,
|
||||||
Node *node, Node *expr,
|
Node *node, Node *expr,
|
||||||
char *colname, bool resjunk);
|
char *colname, bool resjunk);
|
||||||
extern List *ExpandColumnRefStar(ParseState *pstate, ColumnRef *cref,
|
extern Expr *transformAssignedExpr(ParseState *pstate, Expr *expr,
|
||||||
bool targetlist);
|
char *colname,
|
||||||
extern List *ExpandIndirectionStar(ParseState *pstate, A_Indirection *ind,
|
int attrno,
|
||||||
bool targetlist);
|
List *indirection,
|
||||||
|
int location);
|
||||||
extern void updateTargetListEntry(ParseState *pstate, TargetEntry *tle,
|
extern void updateTargetListEntry(ParseState *pstate, TargetEntry *tle,
|
||||||
char *colname, int attrno,
|
char *colname, int attrno,
|
||||||
List *indirection,
|
List *indirection,
|
||||||
|
Loading…
x
Reference in New Issue
Block a user