OSDN Git Service

Add support for multi-row VALUES clauses as part of INSERT statements
authorJoe Conway <mail@joeconway.com>
Wed, 2 Aug 2006 01:59:48 +0000 (01:59 +0000)
committerJoe Conway <mail@joeconway.com>
Wed, 2 Aug 2006 01:59:48 +0000 (01:59 +0000)
(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.

40 files changed:
src/backend/catalog/heap.c
src/backend/commands/explain.c
src/backend/executor/Makefile
src/backend/executor/execAmi.c
src/backend/executor/execProcnode.c
src/backend/executor/nodeValuesscan.c [new file with mode: 0644]
src/backend/nodes/copyfuncs.c
src/backend/nodes/equalfuncs.c
src/backend/nodes/outfuncs.c
src/backend/nodes/print.c
src/backend/nodes/readfuncs.c
src/backend/optimizer/path/allpaths.c
src/backend/optimizer/path/costsize.c
src/backend/optimizer/plan/createplan.c
src/backend/optimizer/plan/planner.c
src/backend/optimizer/plan/setrefs.c
src/backend/optimizer/plan/subselect.c
src/backend/optimizer/util/clauses.c
src/backend/optimizer/util/pathnode.c
src/backend/optimizer/util/plancat.c
src/backend/optimizer/util/relnode.c
src/backend/parser/analyze.c
src/backend/parser/gram.y
src/backend/parser/parse_expr.c
src/backend/parser/parse_node.c
src/backend/parser/parse_relation.c
src/backend/parser/parse_target.c
src/backend/parser/parse_type.c
src/backend/rewrite/rewriteHandler.c
src/backend/utils/adt/ruleutils.c
src/include/catalog/catversion.h
src/include/executor/nodeValuesscan.h [new file with mode: 0644]
src/include/nodes/execnodes.h
src/include/nodes/nodes.h
src/include/nodes/parsenodes.h
src/include/nodes/plannodes.h
src/include/optimizer/cost.h
src/include/optimizer/pathnode.h
src/include/parser/parse_relation.h
src/include/parser/parse_target.h

index 922a0ce..e1f3e19 100644 (file)
@@ -8,7 +8,7 @@
  *
  *
  * 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
@@ -1841,7 +1841,7 @@ cookDefault(ParseState *pstate,
        /*
         * Coerce the expression to the correct type and typmod, if given. This
         * should match the parser's processing of non-defaulted expressions ---
-        * see updateTargetListEntry().
+        * see transformAssignedExpr().
         */
        if (OidIsValid(atttypid))
        {
index 7c50784..5787aa4 100644 (file)
@@ -7,7 +7,7 @@
  * Portions Copyright (c) 1994-5, Regents of the University of California
  *
  * 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:
                        pname = "Function Scan";
                        break;
+               case T_ValuesScan:
+                       pname = "Values Scan";
+                       break;
                case T_Material:
                        pname = "Materialize";
                        break;
@@ -666,6 +669,22 @@ explain_outNode(StringInfo str,
                                                                         quote_identifier(rte->eref->aliasname));
                        }
                        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:
                        break;
        }
@@ -728,6 +747,7 @@ explain_outNode(StringInfo str,
                case T_SeqScan:
                case T_SubqueryScan:
                case T_FunctionScan:
+               case T_ValuesScan:
                        show_scan_qual(plan->qual,
                                                   "Filter",
                                                   ((Scan *) plan)->scanrelid,
index e9caf7d..7bc8eac 100644 (file)
@@ -4,7 +4,7 @@
 #    Makefile for executor
 #
 # IDENTIFICATION
-#    $PostgreSQL: pgsql/src/backend/executor/Makefile,v 1.23 2005/04/19 22:35:11 tgl Exp $
+#    $PostgreSQL: pgsql/src/backend/executor/Makefile,v 1.24 2006/08/02 01:59:45 joe Exp $
 #
 #-------------------------------------------------------------------------
 
@@ -19,7 +19,8 @@ OBJS = execAmi.o execGrouping.o execJunk.o execMain.o \
        nodeBitmapHeapscan.o nodeBitmapIndexscan.o nodeHash.o \
        nodeHashjoin.o nodeIndexscan.o nodeMaterial.o nodeMergejoin.o \
        nodeNestloop.o nodeFunctionscan.o nodeResult.o nodeSeqscan.o \
-       nodeSetOp.o nodeSort.o nodeUnique.o nodeLimit.o nodeGroup.o \
+       nodeSetOp.o nodeSort.o nodeUnique.o \
+       nodeValuesscan.o nodeLimit.o nodeGroup.o \
        nodeSubplan.o nodeSubqueryscan.o nodeTidscan.o tstoreReceiver.o spi.o
 
 all: SUBSYS.o
index df692d9..1d5246f 100644 (file)
@@ -6,7 +6,7 @@
  * Portions Copyright (c) 1996-2006, PostgreSQL Global Development Group
  * Portions Copyright (c) 1994, Regents of the University of California
  *
- *     $PostgreSQL: pgsql/src/backend/executor/execAmi.c,v 1.88 2006/07/14 14:52:18 momjian Exp $
+ *     $PostgreSQL: pgsql/src/backend/executor/execAmi.c,v 1.89 2006/08/02 01:59:45 joe Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -38,6 +38,7 @@
 #include "executor/nodeSubqueryscan.h"
 #include "executor/nodeTidscan.h"
 #include "executor/nodeUnique.h"
+#include "executor/nodeValuesscan.h"
 
 
 /*
@@ -144,6 +145,10 @@ ExecReScan(PlanState *node, ExprContext *exprCtxt)
                        ExecFunctionReScan((FunctionScanState *) node, exprCtxt);
                        break;
 
+               case T_ValuesScanState:
+                       ExecValuesReScan((ValuesScanState *) node, exprCtxt);
+                       break;
+
                case T_NestLoopState:
                        ExecReScanNestLoop((NestLoopState *) node, exprCtxt);
                        break;
@@ -226,6 +231,10 @@ ExecMarkPos(PlanState *node)
                        ExecFunctionMarkPos((FunctionScanState *) node);
                        break;
 
+               case T_ValuesScanState:
+                       ExecValuesMarkPos((ValuesScanState *) node);
+                       break;
+
                case T_MaterialState:
                        ExecMaterialMarkPos((MaterialState *) node);
                        break;
@@ -275,6 +284,10 @@ ExecRestrPos(PlanState *node)
                        ExecFunctionRestrPos((FunctionScanState *) node);
                        break;
 
+               case T_ValuesScanState:
+                       ExecValuesRestrPos((ValuesScanState *) node);
+                       break;
+
                case T_MaterialState:
                        ExecMaterialRestrPos((MaterialState *) node);
                        break;
@@ -298,8 +311,8 @@ ExecRestrPos(PlanState *node)
  *
  * (However, since the only present use of mark/restore is in mergejoin,
  * there is no need to support mark/restore in any plan type that is not
- * capable of generating ordered output.  So the seqscan, tidscan, and
- * functionscan support is actually useless code at present.)
+ * capable of generating ordered output.  So the seqscan, tidscan,
+ * functionscan, and valuesscan support is actually useless code at present.)
  */
 bool
 ExecSupportsMarkRestore(NodeTag plantype)
@@ -310,6 +323,7 @@ ExecSupportsMarkRestore(NodeTag plantype)
                case T_IndexScan:
                case T_TidScan:
                case T_FunctionScan:
+               case T_ValuesScan:
                case T_Material:
                case T_Sort:
                        return true;
@@ -359,6 +373,7 @@ ExecSupportsBackwardScan(Plan *node)
                case T_IndexScan:
                case T_TidScan:
                case T_FunctionScan:
+               case T_ValuesScan:
                        return true;
 
                case T_SubqueryScan:
@@ -413,6 +428,7 @@ ExecMayReturnRawTuples(PlanState *node)
                case T_TidScanState:
                case T_SubqueryScanState:
                case T_FunctionScanState:
+               case T_ValuesScanState:
                        if (node->ps_ProjInfo == NULL)
                                return true;
                        break;
index 77f9f2e..9f1fd54 100644 (file)
@@ -12,7 +12,7 @@
  *
  *
  * IDENTIFICATION
- *       $PostgreSQL: pgsql/src/backend/executor/execProcnode.c,v 1.57 2006/07/14 14:52:18 momjian Exp $
+ *       $PostgreSQL: pgsql/src/backend/executor/execProcnode.c,v 1.58 2006/08/02 01:59:45 joe Exp $
  *
  *-------------------------------------------------------------------------
  */
 #include "executor/nodeSubqueryscan.h"
 #include "executor/nodeTidscan.h"
 #include "executor/nodeUnique.h"
+#include "executor/nodeValuesscan.h"
 #include "miscadmin.h"
 
 /* ------------------------------------------------------------------------
@@ -194,6 +195,11 @@ ExecInitNode(Plan *node, EState *estate, int eflags)
                                                                                                                estate, eflags);
                        break;
 
+               case T_ValuesScan:
+                       result = (PlanState *) ExecInitValuesScan((ValuesScan *) node,
+                                                                                                               estate, eflags);
+                       break;
+
                        /*
                         * join nodes
                         */
@@ -365,6 +371,10 @@ ExecProcNode(PlanState *node)
                        result = ExecFunctionScan((FunctionScanState *) node);
                        break;
 
+               case T_ValuesScanState:
+                       result = ExecValuesScan((ValuesScanState *) node);
+                       break;
+
                        /*
                         * join nodes
                         */
@@ -536,6 +546,9 @@ ExecCountSlotsNode(Plan *node)
                case T_FunctionScan:
                        return ExecCountSlotsFunctionScan((FunctionScan *) node);
 
+               case T_ValuesScan:
+                       return ExecCountSlotsValuesScan((ValuesScan *) node);
+
                        /*
                         * join nodes
                         */
@@ -669,6 +682,10 @@ ExecEndNode(PlanState *node)
                        ExecEndFunctionScan((FunctionScanState *) node);
                        break;
 
+               case T_ValuesScanState:
+                       ExecEndValuesScan((ValuesScanState *) node);
+                       break;
+
                        /*
                         * join nodes
                         */
diff --git a/src/backend/executor/nodeValuesscan.c b/src/backend/executor/nodeValuesscan.c
new file mode 100644 (file)
index 0000000..eb053d8
--- /dev/null
@@ -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;
+}
index f2b2afd..8903d6b 100644 (file)
@@ -15,7 +15,7 @@
  * Portions Copyright (c) 1994, Regents of the University of California
  *
  * 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 $
  *
  *-------------------------------------------------------------------------
  */
@@ -367,6 +367,22 @@ _copyFunctionScan(FunctionScan *from)
 }
 
 /*
+ * _copyValuesScan
+ */
+static ValuesScan *
+_copyValuesScan(ValuesScan *from)
+{
+       ValuesScan *newnode = makeNode(ValuesScan);
+
+       /*
+        * copy node superclass fields
+        */
+       CopyScanFields((Scan *) from, (Scan *) newnode);
+
+       return newnode;
+}
+
+/*
  * CopyJoinFields
  *
  *             This function copies the fields of the Join node.  It is used by
@@ -1356,6 +1372,7 @@ _copyRangeTblEntry(RangeTblEntry *from)
        COPY_NODE_FIELD(funcexpr);
        COPY_NODE_FIELD(funccoltypes);
        COPY_NODE_FIELD(funccoltypmods);
+       COPY_NODE_FIELD(values_lists);
        COPY_SCALAR_FIELD(jointype);
        COPY_NODE_FIELD(joinaliasvars);
        COPY_NODE_FIELD(alias);
@@ -1707,7 +1724,6 @@ _copyInsertStmt(InsertStmt *from)
 
        COPY_NODE_FIELD(relation);
        COPY_NODE_FIELD(cols);
-       COPY_NODE_FIELD(targetList);
        COPY_NODE_FIELD(selectStmt);
 
        return newnode;
@@ -1754,6 +1770,7 @@ _copySelectStmt(SelectStmt *from)
        COPY_NODE_FIELD(whereClause);
        COPY_NODE_FIELD(groupClause);
        COPY_NODE_FIELD(havingClause);
+       COPY_NODE_FIELD(valuesLists);
        COPY_NODE_FIELD(sortClause);
        COPY_NODE_FIELD(limitOffset);
        COPY_NODE_FIELD(limitCount);
@@ -2812,6 +2829,9 @@ copyObject(void *from)
                case T_FunctionScan:
                        retval = _copyFunctionScan(from);
                        break;
+               case T_ValuesScan:
+                       retval = _copyValuesScan(from);
+                       break;
                case T_Join:
                        retval = _copyJoin(from);
                        break;
index 0122ebd..d49e02b 100644 (file)
@@ -18,7 +18,7 @@
  * Portions Copyright (c) 1994, Regents of the University of California
  *
  * 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(cols);
-       COMPARE_NODE_FIELD(targetList);
        COMPARE_NODE_FIELD(selectStmt);
 
        return true;
@@ -723,6 +722,7 @@ _equalSelectStmt(SelectStmt *a, SelectStmt *b)
        COMPARE_NODE_FIELD(whereClause);
        COMPARE_NODE_FIELD(groupClause);
        COMPARE_NODE_FIELD(havingClause);
+       COMPARE_NODE_FIELD(valuesLists);
        COMPARE_NODE_FIELD(sortClause);
        COMPARE_NODE_FIELD(limitOffset);
        COMPARE_NODE_FIELD(limitCount);
@@ -1706,6 +1706,7 @@ _equalRangeTblEntry(RangeTblEntry *a, RangeTblEntry *b)
        COMPARE_NODE_FIELD(funcexpr);
        COMPARE_NODE_FIELD(funccoltypes);
        COMPARE_NODE_FIELD(funccoltypmods);
+       COMPARE_NODE_FIELD(values_lists);
        COMPARE_SCALAR_FIELD(jointype);
        COMPARE_NODE_FIELD(joinaliasvars);
        COMPARE_NODE_FIELD(alias);
index 61d4957..34555e1 100644 (file)
@@ -8,7 +8,7 @@
  *
  *
  * 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
  *       Every node type that can appear in stored rules' parsetrees *must*
@@ -411,6 +411,14 @@ _outFunctionScan(StringInfo str, FunctionScan *node)
 }
 
 static void
+_outValuesScan(StringInfo str, ValuesScan *node)
+{
+       WRITE_NODE_TYPE("VALUESSCAN");
+
+       _outScanInfo(str, (Scan *) node);
+}
+
+static void
 _outJoin(StringInfo str, Join *node)
 {
        WRITE_NODE_TYPE("JOIN");
@@ -1381,6 +1389,7 @@ _outSelectStmt(StringInfo str, SelectStmt *node)
        WRITE_NODE_FIELD(whereClause);
        WRITE_NODE_FIELD(groupClause);
        WRITE_NODE_FIELD(havingClause);
+       WRITE_NODE_FIELD(valuesLists);
        WRITE_NODE_FIELD(sortClause);
        WRITE_NODE_FIELD(limitOffset);
        WRITE_NODE_FIELD(limitCount);
@@ -1591,6 +1600,9 @@ _outRangeTblEntry(StringInfo str, RangeTblEntry *node)
                        WRITE_NODE_FIELD(funccoltypes);
                        WRITE_NODE_FIELD(funccoltypmods);
                        break;
+               case RTE_VALUES:
+                       WRITE_NODE_FIELD(values_lists);
+                       break;
                case RTE_JOIN:
                        WRITE_ENUM_FIELD(jointype, JoinType);
                        WRITE_NODE_FIELD(joinaliasvars);
@@ -1876,6 +1888,9 @@ _outNode(StringInfo str, void *obj)
                        case T_FunctionScan:
                                _outFunctionScan(str, obj);
                                break;
+                       case T_ValuesScan:
+                               _outValuesScan(str, obj);
+                               break;
                        case T_Join:
                                _outJoin(str, obj);
                                break;
index 1a83be6..7fff94a 100644 (file)
@@ -8,7 +8,7 @@
  *
  *
  * 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
  *       AUTHOR                        DATE                    MAJOR EVENT
@@ -275,6 +275,10 @@ print_rt(List *rtable)
                                printf("%d\t%s\t[rangefunction]",
                                           i, rte->eref->aliasname);
                                break;
+                       case RTE_VALUES:
+                               printf("%d\t%s\t[values list]",
+                                          i, rte->eref->aliasname);
+                               break;
                        case RTE_JOIN:
                                printf("%d\t%s\t[join]",
                                           i, rte->eref->aliasname);
@@ -507,6 +511,8 @@ plannode_type(Plan *p)
                        return "SUBQUERYSCAN";
                case T_FunctionScan:
                        return "FUNCTIONSCAN";
+               case T_ValuesScan:
+                       return "VALUESSCAN";
                case T_Join:
                        return "JOIN";
                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);
                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
                extraInfo[0] = '\0';
        if (extraInfo[0] != '\0')
index 265a5b3..7459acb 100644 (file)
@@ -8,7 +8,7 @@
  *
  *
  * 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
  *       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(funccoltypmods);
                        break;
+               case RTE_VALUES:
+                       READ_NODE_FIELD(values_lists);
+                       break;
                case RTE_JOIN:
                        READ_ENUM_FIELD(jointype, JoinType);
                        READ_NODE_FIELD(joinaliasvars);
index 05fe268..1d7e20c 100644 (file)
@@ -8,7 +8,7 @@
  *
  *
  * 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);
 static void set_function_pathlist(PlannerInfo *root, RelOptInfo *rel,
                                          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_one_rel_by_joins(PlannerInfo *root, int levels_needed,
                                          List *initial_rels);
@@ -170,6 +172,11 @@ set_rel_pathlist(PlannerInfo *root, RelOptInfo *rel, Index rti)
                /* RangeFunction --- generate a separate plan for it */
                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
        {
                /* Plain relation */
@@ -538,6 +545,23 @@ set_function_pathlist(PlannerInfo *root, RelOptInfo *rel, RangeTblEntry *rte)
 }
 
 /*
+ * 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
  *       Build access paths using a "joinlist" to guide the join path search.
  *
index 5f5e9ec..fffa25d 100644 (file)
@@ -54,7 +54,7 @@
  * Portions Copyright (c) 1994, Regents of the University of California
  *
  * 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 $
  *
  *-------------------------------------------------------------------------
  */
@@ -775,6 +775,36 @@ cost_functionscan(Path *path, PlannerInfo *root, RelOptInfo *baserel)
 }
 
 /*
+ * 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
  *       Determines and returns the cost of sorting a relation, including
  *       the cost of reading the input data.
@@ -2023,6 +2053,37 @@ set_function_size_estimates(PlannerInfo *root, RelOptInfo *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
index 7ed718f..ae51505 100644 (file)
@@ -10,7 +10,7 @@
  *
  *
  * 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);
 static FunctionScan *create_functionscan_plan(PlannerInfo *root, Path *best_path,
                                                 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,
                                         Plan *outer_plan, Plan *inner_plan);
 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);
 static FunctionScan *make_functionscan(List *qptlist, List *qpqual,
                                  Index scanrelid);
+static ValuesScan *make_valuesscan(List *qptlist, List *qpqual,
+                                 Index scanrelid);
 static BitmapAnd *make_bitmap_and(List *bitmapplans);
 static BitmapOr *make_bitmap_or(List *bitmapplans);
 static NestLoop *make_nestloop(List *tlist,
@@ -146,6 +150,7 @@ create_plan(PlannerInfo *root, Path *best_path)
                case T_TidScan:
                case T_SubqueryScan:
                case T_FunctionScan:
+               case T_ValuesScan:
                        plan = create_scan_plan(root, best_path);
                        break;
                case T_HashJoin:
@@ -262,6 +267,13 @@ create_scan_plan(PlannerInfo *root, Path *best_path)
                                                                                                         scan_clauses);
                        break;
 
+               case T_ValuesScan:
+                       plan = (Plan *) create_valuesscan_plan(root,
+                                                                                                  best_path,
+                                                                                                  tlist,
+                                                                                                  scan_clauses);
+                       break;
+
                default:
                        elog(ERROR, "unrecognized node type: %d",
                                 (int) best_path->pathtype);
@@ -315,12 +327,13 @@ use_physical_tlist(RelOptInfo *rel)
        int                     i;
 
        /*
-        * We can do this for real relation scans, subquery scans, and function
-        * scans (but not for, eg, joins).
+        * We can do this for real relation scans, subquery scans, function
+        * scans, and values scans (but not for, eg, joins).
         */
        if (rel->rtekind != RTE_RELATION &&
                rel->rtekind != RTE_SUBQUERY &&
-               rel->rtekind != RTE_FUNCTION)
+               rel->rtekind != RTE_FUNCTION &&
+               rel->rtekind != RTE_VALUES)
                return false;
 
        /*
@@ -365,6 +378,7 @@ disuse_physical_tlist(Plan *plan, Path *path)
                case T_TidScan:
                case T_SubqueryScan:
                case T_FunctionScan:
+               case T_ValuesScan:
                        plan->targetlist = build_relation_tlist(path->parent);
                        break;
                default:
@@ -1312,6 +1326,35 @@ create_functionscan_plan(PlannerInfo *root, Path *best_path,
        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
@@ -2123,6 +2166,24 @@ make_functionscan(List *qptlist,
        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 *
 make_append(List *appendplans, bool isTarget, List *tlist)
 {
index c02f6e1..42ae15c 100644 (file)
@@ -8,7 +8,7 @@
  *
  *
  * 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_TARGET                1
 #define EXPRKIND_RTFUNC                2
-#define EXPRKIND_LIMIT         3
-#define EXPRKIND_ININFO                4
-#define EXPRKIND_APPINFO       5
+#define EXPRKIND_VALUES                3
+#define EXPRKIND_LIMIT         4
+#define EXPRKIND_ININFO                5
+#define EXPRKIND_APPINFO       6
 
 
 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,
                                                          EXPRKIND_APPINFO);
 
-       /* Also need to preprocess expressions for function RTEs */
+       /* Also need to preprocess expressions for function and values RTEs */
        foreach(l, parse->rtable)
        {
                RangeTblEntry *rte = (RangeTblEntry *) lfirst(l);
@@ -303,6 +304,10 @@ subquery_planner(Query *parse, double tuple_fraction,
                if (rte->rtekind == RTE_FUNCTION)
                        rte->funcexpr = preprocess_expression(root, rte->funcexpr,
                                                                                                  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
         * base-relation variables. We must do this before sublink processing,
         * 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);
 
        /*
@@ -437,10 +444,14 @@ preprocess_expression(PlannerInfo *root, Node *expr, int kind)
         * 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
         * 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 ||
-               kind == EXPRKIND_QUAL ||
-               PlannerQueryLevel > 1)
+       if (kind != EXPRKIND_VALUES &&
+               (root->parse->jointree->fromlist != NIL ||
+                kind == EXPRKIND_QUAL ||
+                PlannerQueryLevel > 1))
                expr = eval_const_expressions(expr);
 
        /*
@@ -465,7 +476,7 @@ preprocess_expression(PlannerInfo *root, Node *expr, int kind)
         * SS_replace_correlation_vars ...
         */
 
-       /* Replace uplevel vars with Param nodes */
+       /* Replace uplevel vars with Param nodes (this IS possible in VALUES) */
        if (PlannerQueryLevel > 1)
                expr = SS_replace_correlation_vars(expr);
 
index 2529c42..11ef476 100644 (file)
@@ -9,7 +9,7 @@
  *
  *
  * 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);
                        }
                        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:
                        set_join_references((Join *) plan, rtable);
                        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);
                        /* rte was already fixed by set_subqueryscan_references */
                        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:
                        adjust_expr_varnos((Node *) plan->targetlist, rtoffset);
                        adjust_expr_varnos((Node *) plan->qual, rtoffset);
index 0bc9a46..95e5604 100644 (file)
@@ -7,7 +7,7 @@
  * Portions Copyright (c) 1994, Regents of the University of California
  *
  * 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;
 
+               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:
                        {
                                ListCell   *l;
index dfc4314..5570b33 100644 (file)
@@ -8,7 +8,7 @@
  *
  *
  * 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
  *       AUTHOR                        DATE                    MAJOR EVENT
@@ -3351,6 +3351,10 @@ range_table_walker(List *rtable,
                                if (walker(rte->funcexpr, context))
                                        return true;
                                break;
+                       case RTE_VALUES:
+                               if (walker(rte->values_lists, context))
+                                       return true;
+                               break;
                }
        }
        return false;
@@ -3917,6 +3921,9 @@ range_table_mutator(List *rtable,
                        case RTE_FUNCTION:
                                MUTATE(newrte->funcexpr, rte->funcexpr, Node *);
                                break;
+                       case RTE_VALUES:
+                               MUTATE(newrte->values_lists, rte->values_lists, List *);
+                               break;
                }
                newrt = lappend(newrt, newrte);
        }
index 631d608..2cc79ed 100644 (file)
@@ -8,7 +8,7 @@
  *
  *
  * 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 $
  *
  *-------------------------------------------------------------------------
  */
@@ -1083,6 +1083,25 @@ create_functionscan_path(PlannerInfo *root, RelOptInfo *rel)
 }
 
 /*
+ * 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
  *       Creates a pathnode corresponding to a nestloop join between two
  *       relations.
index bafe1b6..f74faa5 100644 (file)
@@ -9,7 +9,7 @@
  *
  *
  * 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
  * dropped cols.
  *
- * We also support building a "physical" tlist for subqueries and functions,
- * since the same optimization can occur in SubqueryScan and FunctionScan
- * nodes.
+ * We also support building a "physical" tlist for subqueries, functions,
+ * and values lists, since the same optimization can occur in SubqueryScan,
+ * FunctionScan, and ValuesScan nodes.
  */
 List *
 build_physical_tlist(PlannerInfo *root, RelOptInfo *rel)
@@ -594,6 +594,21 @@ build_physical_tlist(PlannerInfo *root, RelOptInfo *rel)
                        }
                        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:
                        /* caller error */
                        elog(ERROR, "unsupported RTE kind %d in build_physical_tlist",
index 8d06254..545b125 100644 (file)
@@ -8,7 +8,7 @@
  *
  *
  * 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;
                case RTE_SUBQUERY:
                case RTE_FUNCTION:
-                       /* Subquery or function --- set up attr range and arrays */
-                       /* Note: 0 is included in range to support whole-row Vars */
+               case RTE_VALUES:
+                       /*
+                        * 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->max_attr = list_length(rte->eref->colnames);
                        rel->attr_needed = (Relids *)
index b086afe..4f7001b 100644 (file)
@@ -6,7 +6,7 @@
  * Portions Copyright (c) 1996-2006, PostgreSQL Global Development Group
  * Portions Copyright (c) 1994, Regents of the University of California
  *
- *     $PostgreSQL: pgsql/src/backend/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 *transformInsertStmt(ParseState *pstate, InsertStmt *stmt,
                                        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 *transformRuleStmt(ParseState *query, RuleStmt *stmt,
                                  List **extras_before, List **extras_after);
 static Query *transformSelectStmt(ParseState *pstate, SelectStmt *stmt);
+static Query *transformValuesClause(ParseState *pstate, SelectStmt *stmt);
 static Query *transformSetOperationStmt(ParseState *pstate, SelectStmt *stmt);
 static Node *transformSetOperationTree(ParseState *pstate, SelectStmt *stmt);
 static Query *transformUpdateStmt(ParseState *pstate, UpdateStmt *stmt);
@@ -367,12 +370,16 @@ transformStmt(ParseState *pstate, Node *parseTree,
                        break;
 
                case T_SelectStmt:
-                       if (((SelectStmt *) parseTree)->op == SETOP_NONE)
-                               result = transformSelectStmt(pstate,
-                                                                                        (SelectStmt *) parseTree);
-                       else
-                               result = transformSetOperationStmt(pstate,
-                                                                                                  (SelectStmt *) parseTree);
+                       {
+                               SelectStmt *n = (SelectStmt *) parseTree;
+
+                               if (n->valuesLists)
+                                       result = transformValuesClause(pstate, n);
+                               else if (n->op == SETOP_NONE)
+                                       result = transformSelectStmt(pstate, n);
+                               else
+                                       result = transformSetOperationStmt(pstate, n);
+                       }
                        break;
 
                case T_DeclareCursorStmt:
@@ -510,20 +517,31 @@ transformInsertStmt(ParseState *pstate, InsertStmt *stmt,
                                        List **extras_before, List **extras_after)
 {
        Query      *qry = makeNode(Query);
-       Query      *selectQuery = NULL;
+       SelectStmt *selectStmt = (SelectStmt *) stmt->selectStmt;
+       List       *exprList = NIL;
+       bool            isGeneralSelect;
        List       *sub_rtable;
        List       *sub_relnamespace;
        List       *sub_varnamespace;
        List       *icolumns;
        List       *attrnos;
+       RangeTblEntry *rte;
+       RangeTblRef *rtr;
        ListCell   *icols;
        ListCell   *attnos;
-       ListCell   *tl;
+       ListCell   *lc;
 
        qry->commandType = CMD_INSERT;
        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
         * INSERT/SELECT, arrange to pass the rangetable/namespace down to the
         * SELECT.      This can only happen if we are inside a CREATE RULE, and in
@@ -532,7 +550,7 @@ transformInsertStmt(ParseState *pstate, InsertStmt *stmt,
         * The SELECT's joinlist is not affected however.  We must do this before
         * adding the target table to the INSERT's rtable.
         */
-       if (stmt->selectStmt)
+       if (isGeneralSelect)
        {
                sub_rtable = pstate->p_rtable;
                pstate->p_rtable = NIL;
@@ -557,10 +575,23 @@ transformInsertStmt(ParseState *pstate, InsertStmt *stmt,
        qry->resultRelation = setTargetTable(pstate, stmt->relation,
                                                                                 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
@@ -570,8 +601,7 @@ transformInsertStmt(ParseState *pstate, InsertStmt *stmt,
                 * see.
                 */
                ParseState *sub_pstate = make_parsestate(pstate);
-               RangeTblEntry *rte;
-               RangeTblRef *rtr;
+               Query      *selectQuery;
 
                /*
                 * Process the source SELECT.
@@ -617,8 +647,8 @@ transformInsertStmt(ParseState *pstate, InsertStmt *stmt,
                pstate->p_joinlist = lappend(pstate->p_joinlist, rtr);
 
                /*----------
-                * Generate a targetlist for the INSERT that selects all the
-                * non-resjunk columns from the subquery.  (We need this to be
+                * Generate an expression list for the INSERT that selects all the
+                * non-resjunk columns from the subquery.  (INSERT's tlist must be
                 * separate from the subquery's tlist because we may add columns,
                 * insert datatype coercions, etc.)
                 *
@@ -630,10 +660,10 @@ transformInsertStmt(ParseState *pstate, InsertStmt *stmt,
                 *              INSERT INTO foo SELECT 'bar', ... FROM baz
                 *----------
                 */
-               qry->targetList = NIL;
-               foreach(tl, selectQuery->targetList)
+               exprList = NIL;
+               foreach(lc, selectQuery->targetList)
                {
-                       TargetEntry *tle = (TargetEntry *) lfirst(tl);
+                       TargetEntry *tle = (TargetEntry *) lfirst(lc);
                        Expr       *expr;
 
                        if (tle->resjunk)
@@ -648,83 +678,221 @@ transformInsertStmt(ParseState *pstate, InsertStmt *stmt,
                                                                                exprType((Node *) tle->expr),
                                                                                exprTypmod((Node *) tle->expr),
                                                                                0);
-                       tle = makeTargetEntry(expr,
-                                                                 (AttrNumber) pstate->p_next_resno++,
-                                                                 tle->resname,
-                                                                 false);
-                       qry->targetList = lappend(qry->targetList, tle);
+                       exprList = lappend(exprList, expr);
                }
+
+               /* Prepare row for assignment to target table */
+               exprList = transformInsertRow(pstate, exprList,
+                                                                         stmt->cols,
+                                                                         icolumns, attrnos);
        }
-       else
+       else if (list_length(selectStmt->valuesLists) > 1)
        {
                /*
-                * For INSERT ... VALUES, transform the given list of values to form a
-                * targetlist for the INSERT.
+                * 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.
                 */
-               qry->targetList = transformTargetList(pstate, stmt->targetList);
+               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
+       {
+               /*----------
+                * Process INSERT ... VALUES with a single VALUES sublist.
+                * 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.
+                *----------
+                */
+               List       *valuesLists = selectStmt->valuesLists;
 
-       /*
-        * Now we are done with SELECT-like processing, and can get on with
-        * transforming the target list to match the INSERT target columns.
-        */
+               Assert(list_length(valuesLists) == 1);
 
-       /* 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;
+               /* Do basic expression transformation (same as a ROW() expr) */
+               exprList = transformExpressionList(pstate,
+                                                                                  (List *) linitial(valuesLists));
 
-       /* Validate stmt->cols list, or build default list if no list given */
-       icolumns = checkInsertTargets(pstate, stmt->cols, &attrnos);
+               /* Prepare row for assignment to target table */
+               exprList = transformInsertRow(pstate, exprList,
+                                                                         stmt->cols,
+                                                                         icolumns, attrnos);
+       }
 
        /*
-        * Prepare columns for assignment to target table.
+        * Generate query's target list using the computed list of expressions.
         */
+       qry->targetList = NIL;
        icols = list_head(icolumns);
        attnos = list_head(attrnos);
-       foreach(tl, qry->targetList)
+       foreach(lc, exprList)
        {
-               TargetEntry *tle = (TargetEntry *) lfirst(tl);
+               Expr *expr = (Expr *) lfirst(lc);
                ResTarget  *col;
-
-               if (icols == NULL || attnos == NULL)
-                       ereport(ERROR,
-                                       (errcode(ERRCODE_SYNTAX_ERROR),
-                                errmsg("INSERT has more expressions than target columns")));
+               TargetEntry *tle;
 
                col = (ResTarget *) lfirst(icols);
                Assert(IsA(col, ResTarget));
 
-               Assert(!tle->resjunk);
-               updateTargetListEntry(pstate, tle, col->name, lfirst_int(attnos),
-                                                         col->indirection, col->location);
+               tle = makeTargetEntry(expr,
+                                                         (AttrNumber) lfirst_int(attnos),
+                                                         col->name,
+                                                         false);
+               qry->targetList = lappend(qry->targetList, tle);
 
                icols = lnext(icols);
                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 */
        qry->rtable = pstate->p_rtable;
        qry->jointree = makeFromExpr(pstate->p_joinlist, NULL);
 
        qry->hasSubLinks = pstate->p_hasSubLinks;
-       qry->hasAggs = pstate->p_hasAggs;
+       /* aggregates not allowed (but subselects are okay) */
        if (pstate->p_hasAggs)
-               parseCheckAggregates(pstate, qry);
+               ereport(ERROR,
+                               (errcode(ERRCODE_GROUPING_ERROR),
+                                errmsg("cannot use aggregate function in VALUES")));
 
        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 -
  *       transforms the "create table" statement
  *       SQL92 allows constraints to be scattered all over, so thumb through
@@ -1934,6 +2102,187 @@ transformSelectStmt(ParseState *pstate, SelectStmt *stmt)
 }
 
 /*
+ * 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 -
  *       transforms a set-operations tree
  *
@@ -2931,6 +3280,11 @@ transformLockingClause(Query *qry, LockingClause *lc)
                                                                        (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
                                                                         errmsg("SELECT FOR UPDATE/SHARE cannot be applied to a function")));
                                                        break;
+                                               case RTE_VALUES:
+                                                       ereport(ERROR,
+                                                                       (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
+                                                                        errmsg("SELECT FOR UPDATE/SHARE cannot be applied to VALUES")));
+                                                       break;
                                                default:
                                                        elog(ERROR, "unrecognized RTE type: %d",
                                                                 (int) rte->rtekind);
index 434f05b..4586c77 100644 (file)
@@ -11,7 +11,7 @@
  *
  *
  * 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
  *       AUTHOR                        DATE                    MAJOR EVENT
@@ -173,7 +173,7 @@ static void doNegateFloat(Value *v);
                DropOwnedStmt ReassignOwnedStmt
 
 %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 <ival>   add_drop
@@ -238,7 +238,7 @@ static void doNegateFloat(Value *v);
                                qualified_name_list any_name any_name_list
                                any_operator expr_list attrs
                                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
                                opt_select_limit opclass_item_list
                                transaction_mode_list_or_empty
@@ -299,7 +299,7 @@ static void doNegateFloat(Value *v);
 %type <list>   when_clause_list
 %type <ival>   sub_type
 %type <list>   OptCreateAs CreateAsList
-%type <node>   CreateAsElement
+%type <node>   CreateAsElement values_item
 %type <value>  NumericOnly FloatOnly IntegerOnly
 %type <alias>  alias_clause
 %type <sortby> sortby
@@ -308,7 +308,7 @@ static void doNegateFloat(Value *v);
 %type <jexpr>  joined_table
 %type <range>  relation_expr
 %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
                                GenericType Numeric opt_float
@@ -5342,40 +5342,23 @@ InsertStmt:
                ;
 
 insert_rest:
-                       VALUES '(' insert_target_list ')'
-                               {
-                                       $$ = makeNode(InsertStmt);
-                                       $$->cols = NIL;
-                                       $$->targetList = $3;
-                                       $$->selectStmt = NULL;
-                               }
-                       | DEFAULT VALUES
-                               {
-                                       $$ = makeNode(InsertStmt);
-                                       $$->cols = NIL;
-                                       $$->targetList = NIL;
-                                       $$->selectStmt = NULL;
-                               }
-                       | SelectStmt
+                       SelectStmt
                                {
                                        $$ = makeNode(InsertStmt);
                                        $$->cols = NIL;
-                                       $$->targetList = NIL;
                                        $$->selectStmt = $1;
                                }
-                       | '(' insert_column_list ')' VALUES '(' insert_target_list ')'
+                       | '(' insert_column_list ')' SelectStmt
                                {
                                        $$ = makeNode(InsertStmt);
                                        $$->cols = $2;
-                                       $$->targetList = $6;
-                                       $$->selectStmt = NULL;
+                                       $$->selectStmt = $4;
                                }
-                       | '(' insert_column_list ')' SelectStmt
+                       | DEFAULT VALUES
                                {
                                        $$ = makeNode(InsertStmt);
-                                       $$->cols = $2;
-                                       $$->targetList = NIL;
-                                       $$->selectStmt = $4;
+                                       $$->cols = NIL;
+                                       $$->selectStmt = NULL;
                                }
                ;
 
@@ -5629,6 +5612,7 @@ simple_select:
                                        n->havingClause = $8;
                                        $$ = (Node *)n;
                                }
+                       | values_clause                                                 { $$ = $1; }
                        | select_clause UNION opt_all select_clause
                                {
                                        $$ = makeSetOp(SETOP_UNION, $3, $1, $4);
@@ -5848,6 +5832,32 @@ locked_rels_list:
                        | /* 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:
@@ -5937,10 +5947,17 @@ table_ref:      relation_expr
                                         * However, it does seem like a good idea to emit
                                         * an error message that's better than "syntax error".
                                         */
-                                       ereport(ERROR,
-                                                       (errcode(ERRCODE_SYNTAX_ERROR),
-                                                        errmsg("subquery in FROM must have an alias"),
-                                                        errhint("For example, FROM (SELECT ...) [AS] foo.")));
+                                       if (IsA($1, SelectStmt) &&
+                                               ((SelectStmt *) $1)->valuesLists)
+                                               ereport(ERROR,
+                                                               (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;
                                }
                        | 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
                        | VALID
                        | VALIDATOR
-                       | VALUES
                        | VARYING
                        | VIEW
                        | VOLATILE
@@ -8715,6 +8707,7 @@ col_name_keyword:
                        | TIMESTAMP
                        | TREAT
                        | TRIM
+                       | VALUES
                        | VARCHAR
                ;
 
index 11ef7e4..6a7117e 100644 (file)
@@ -8,7 +8,7 @@
  *
  *
  * 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)
 {
        RowExpr    *newr = makeNode(RowExpr);
-       List       *newargs = NIL;
-       ListCell   *arg;
 
        /* Transform the field expressions */
-       foreach(arg, 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;
+       newr->args = transformExpressionList(pstate, r->args);
 
        /* Barring later casting, we consider the type RECORD */
        newr->row_typeid = RECORDOID;
@@ -1526,6 +1479,15 @@ transformWholeRowRef(ParseState *pstate, char *schemaname, char *relname,
                                                                                  sublevels_up);
                        }
                        break;
+               case RTE_VALUES:
+                       toid = RECORDOID;
+                       /* returns composite; same as relation case */
+                       result = (Node *) makeVar(vnum,
+                                                                         InvalidAttrNumber,
+                                                                         toid,
+                                                                         -1,
+                                                                         sublevels_up);
+                       break;
                default:
 
                        /*
index f1b6a45..9c1b587 100644 (file)
@@ -8,7 +8,7 @@
  *
  *
  * 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.
-        * (This should agree with the coercion done by updateTargetListEntry.)
+        * (This should agree with the coercion done by transformAssignedExpr.)
         */
        if (assignFrom != NULL)
        {
index 10f7171..e9896be 100644 (file)
@@ -8,7 +8,7 @@
  *
  *
  * 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 $
  *
  *-------------------------------------------------------------------------
  */
@@ -941,6 +941,75 @@ addRangeTableEntryForFunction(ParseState *pstate,
 }
 
 /*
+ * 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).
  *
  * This is much like addRangeTableEntry() except that it makes a join RTE.
@@ -1233,6 +1302,41 @@ expandRTE(RangeTblEntry *rte, int rtindex, int sublevels_up,
                                }
                        }
                        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:
                        {
                                /* Join RTE */
@@ -1569,6 +1673,20 @@ get_rte_attribute_type(RangeTblEntry *rte, AttrNumber attnum,
                                }
                        }
                        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:
                        {
                                /*
@@ -1619,7 +1737,8 @@ get_rte_attribute_is_dropped(RangeTblEntry *rte, AttrNumber attnum)
                        }
                        break;
                case RTE_SUBQUERY:
-                       /* Subselect RTEs never have dropped columns */
+               case RTE_VALUES:
+                       /* Subselect and Values RTEs never have dropped columns */
                        result = false;
                        break;
                case RTE_JOIN:
index 6f2e44f..9258acc 100644 (file)
@@ -8,7 +8,7 @@
  *
  *
  * 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,
                                                           Node *rhs,
                                                           int location);
+static List *ExpandColumnRefStar(ParseState *pstate, ColumnRef *cref,
+                                       bool targetlist);
 static List *ExpandAllTables(ParseState *pstate);
+static List *ExpandIndirectionStar(ParseState *pstate, A_Indirection *ind,
+                                         bool targetlist);
 static int     FigureColnameInternal(Node *node, char **name);
 
 
@@ -152,6 +156,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()
  *             Mark targetlist columns that are simple Vars with the source
  *             table's OID and column number.
@@ -229,6 +296,7 @@ markTargetListOrigin(ParseState *pstate, TargetEntry *tle,
                        break;
                case RTE_SPECIAL:
                case RTE_FUNCTION:
+               case RTE_VALUES:
                        /* not a simple relation, leave it unmarked */
                        break;
        }
@@ -236,23 +304,26 @@ markTargetListOrigin(ParseState *pstate, TargetEntry *tle,
 
 
 /*
- * updateTargetListEntry()
- *     This is used in INSERT and UPDATE statements only.      It prepares a
- *     TargetEntry for assignment to a column of the target table.
+ * transformAssignedExpr()
+ *     This is used in INSERT and UPDATE statements only.      It prepares an
+ *     expression 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.
+ *     attached to the target column itself.  The input expression has
+ *     already been through transformExpr().
  *
  * 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)
  * attrno              target attribute number
  * 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
-updateTargetListEntry(ParseState *pstate,
-                                         TargetEntry *tle,
+Expr *
+transformAssignedExpr(ParseState *pstate,
+                                         Expr *expr,
                                          char *colname,
                                          int attrno,
                                          List *indirection,
@@ -281,9 +352,9 @@ updateTargetListEntry(ParseState *pstate,
         * or array element with DEFAULT, since there can't be any default for
         * 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->typeMod = attrtypmod;
@@ -303,7 +374,7 @@ updateTargetListEntry(ParseState *pstate,
        }
 
        /* 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
@@ -334,7 +405,7 @@ updateTargetListEntry(ParseState *pstate,
                                                                           attrno);
                }
 
-               tle->expr = (Expr *)
+               expr = (Expr *)
                        transformAssignmentIndirection(pstate,
                                                                                   colVar,
                                                                                   colname,
@@ -342,7 +413,7 @@ updateTargetListEntry(ParseState *pstate,
                                                                                   attrtype,
                                                                                   attrtypmod,
                                                                                   list_head(indirection),
-                                                                                  (Node *) tle->expr,
+                                                                                  (Node *) expr,
                                                                                   location);
        }
        else
@@ -351,13 +422,13 @@ updateTargetListEntry(ParseState *pstate,
                 * For normal non-qualified target column, do type checking and
                 * coercion.
                 */
-               tle->expr = (Expr *)
+               expr = (Expr *)
                        coerce_to_target_type(pstate,
-                                                                 (Node *) tle->expr, type_id,
+                                                                 (Node *) expr, type_id,
                                                                  attrtype, attrtypmod,
                                                                  COERCION_ASSIGNMENT,
                                                                  COERCE_IMPLICIT_CAST);
-               if (tle->expr == NULL)
+               if (expr == NULL)
                        ereport(ERROR,
                                        (errcode(ERRCODE_DATATYPE_MISMATCH),
                                         errmsg("column \"%s\" is of type %s"
@@ -369,6 +440,41 @@ updateTargetListEntry(ParseState *pstate,
                                         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
         * planner depend on this.      We also set the resname to identify the target
@@ -379,6 +485,7 @@ updateTargetListEntry(ParseState *pstate,
        tle->resname = colname;
 }
 
+
 /*
  * Process indirection (field selection or subscripting) of the target
  * 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
  * 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)
- * 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,
                                        bool targetlist)
 {
@@ -836,9 +944,9 @@ ExpandAllTables(ParseState *pstate)
  * 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
  * 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,
                                          bool targetlist)
 {
@@ -996,11 +1104,12 @@ expandRecordVariable(ParseState *pstate, Var *var, int levelsup)
        {
                case RTE_RELATION:
                case RTE_SPECIAL:
+               case RTE_VALUES:
 
                        /*
-                        * This case should not occur: a column of a table shouldn't have
-                        * type RECORD.  Fall through and fail (most likely) at the
-                        * bottom.
+                        * This case should not occur: a column of a table or values list
+                        * shouldn't have type RECORD.  Fall through and fail
+                        * (most likely) at the bottom.
                         */
                        break;
                case RTE_SUBQUERY:
index b6eac74..a12aea6 100644 (file)
@@ -8,7 +8,7 @@
  *
  *
  * 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->groupClause != NIL ||
                stmt->havingClause != NULL ||
+               stmt->valuesLists != NIL ||
                stmt->sortClause != NIL ||
                stmt->limitOffset != NULL ||
                stmt->limitCount != NULL ||
index 42c965f..286fc5b 100644 (file)
@@ -7,7 +7,7 @@
  * Portions Copyright (c) 1994, Regents of the University of California
  *
  * 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,
                                  CmdType event);
 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,
                                        TargetEntry *prior_tle,
                                        const char *attrName);
 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,
                                        bool skipOldNew);
 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
  * 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.
+ *
+ * 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
-rewriteTargetList(Query *parsetree, Relation target_relation)
+rewriteTargetList(Query *parsetree, Relation target_relation,
+                                 List **attrno_list)
 {
        CmdType         commandType = parsetree->commandType;
        TargetEntry **new_tles;
@@ -494,6 +503,9 @@ rewriteTargetList(Query *parsetree, Relation target_relation)
                                numattrs;
        ListCell   *temp;
 
+       if (attrno_list)                        /* initialize optional result list */
+               *attrno_list = NIL;
+
        /*
         * We process the normal (non-junk) attributes by scanning the input tlist
         * 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);
                        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 */
                        if (att_tup->attisdropped)
                                continue;
@@ -820,7 +836,7 @@ build_column_default(Relation rel, int attrno)
         * generally be true already, but there seem to be some corner cases
         * involving domain defaults where it might not be true. This should match
         * the parser's processing of non-defaulted expressions --- see
-        * updateTargetListEntry().
+        * transformAssignedExpr().
         */
        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 -
  *       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
                 * ensures that any references to NEW.field will behave sanely.
                 */
-               if (event == CMD_INSERT || event == CMD_UPDATE)
-                       rewriteTargetList(parsetree, rt_entry_relation);
+               if (event == CMD_UPDATE)
+                       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.
index a2254b6..8228fb2 100644 (file)
@@ -2,7 +2,7 @@
  * ruleutils.c - Functions to convert stored expressions/querytrees
  *                             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"
@@ -131,6 +131,7 @@ static void make_viewdef(StringInfo buf, HeapTuple ruletup, TupleDesc rulettc,
                         int prettyFlags);
 static void get_query_def(Query *query, StringInfo buf, List *parentnamespace,
                          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,
                                         TupleDesc resultDesc);
 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);
 static void get_opclass_name(Oid opclass, Oid actual_datatype,
                                 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 char *generate_relation_name(Oid relid);
 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
@@ -1910,14 +1956,37 @@ get_basic_select_query(Query *query, deparse_context *context,
        ListCell   *l;
        int                     colno;
 
-       /*
-        * Build up the query string - first we say SELECT
-        */
        if (PRETTY_INDENT(context))
        {
                context->indentLevel += PRETTYINDENT_STD;
                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");
 
        /* Add the DISTINCT clause if given */
@@ -2191,24 +2260,37 @@ get_insert_query_def(Query *query, deparse_context *context)
 {
        StringInfo      buf = context->buf;
        RangeTblEntry *select_rte = NULL;
+       RangeTblEntry *values_rte = NULL;
        RangeTblEntry *rte;
        char       *sep;
+       ListCell   *values_cell;
        ListCell   *l;
        List       *strippedexprs;
 
        /*
-        * If it's an INSERT ... SELECT there will be a single subquery RTE for
-        * the SELECT.
+        * If it's an INSERT ... SELECT or VALUES (...), (...), ...
+        * there will be a single RTE for the SELECT or VALUES.
         */
        foreach(l, query->rtable)
        {
                rte = (RangeTblEntry *) lfirst(l);
-               if (rte->rtekind != RTE_SUBQUERY)
-                       continue;
-               if (select_rte)
-                       elog(ERROR, "too many RTEs in INSERT");
-               select_rte = rte;
+
+               if (rte->rtekind == RTE_SUBQUERY)
+               {
+                       if (select_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
@@ -2225,9 +2307,17 @@ get_insert_query_def(Query *query, deparse_context *context)
                                         generate_relation_name(rte->relid));
 
        /*
-        * Add the insert-column-names list, and make a list of the actual
-        * assignment source expressions.
+        * Add the insert-column-names list.  To handle indirection properly,
+        * 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;
        sep = "";
        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
                 * off the top-level nodes representing the indirection assignments.
                 */
-               strippedexprs = lappend(strippedexprs,
-                                                               processIndirection((Node *) tle->expr,
-                                                                                                  context));
+               if (values_cell)
+               {
+                       /* 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, ") ");
 
-       /* Add the VALUES or the SELECT */
-       if (select_rte == NULL)
+       if (select_rte)
+       {
+               /* 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 (",
                                                         -PRETTYINDENT_STD, PRETTYINDENT_STD, 2);
                get_rule_expr((Node *) strippedexprs, context, false);
                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
                 * off the top-level nodes representing the indirection assignments.
                 */
-               expr = processIndirection((Node *) tle->expr, context);
+               expr = processIndirection((Node *) tle->expr, context, true);
 
                appendStringInfo(buf, " = ");
 
@@ -2612,11 +2720,12 @@ get_name_for_var_field(Var *var, int fieldno,
        switch (rte->rtekind)
        {
                case RTE_RELATION:
+               case RTE_VALUES:
 
                        /*
-                        * This case should not occur: a column of a table shouldn't have
-                        * type RECORD.  Fall through and fail (most likely) at the
-                        * bottom.
+                        * This case should not occur: a column of a table or values list
+                        * shouldn't have type RECORD.  Fall through and fail
+                        * (most likely) at the bottom.
                         */
                        break;
                case RTE_SUBQUERY:
@@ -4232,6 +4341,10 @@ get_from_clause_item(Node *jtnode, Query *query, deparse_context *context)
                                /* Function RTE */
                                get_rule_expr(rte->funcexpr, context, true);
                                break;
+                       case RTE_VALUES:
+                               /* Values list RTE */
+                               get_values_def(rte->values_lists, context);
+                               break;
                        default:
                                elog(ERROR, "unrecognized RTE kind: %d", (int) rte->rtekind);
                                break;
@@ -4576,12 +4689,12 @@ get_opclass_name(Oid opclass, Oid actual_datatype,
  * processIndirection - take care of array and subfield assignment
  *
  * We strip any top-level FieldStore or assignment ArrayRef nodes that
- * appear in the input, printing out the appropriate decoration for the
- * base column name (that the caller just printed).  We return the
- * subexpression that's to be assigned.
+ * appear in the input, and return the subexpression that's to be assigned.
+ * If printit is true, we also print out the appropriate decoration for the
+ * base column name (that the caller just printed).
  */
 static Node *
-processIndirection(Node *node, deparse_context *context)
+processIndirection(Node *node, deparse_context *context, bool printit)
 {
        StringInfo      buf = context->buf;
 
@@ -4602,15 +4715,16 @@ processIndirection(Node *node, deparse_context *context)
                                         format_type_be(fstore->resulttype));
 
                        /*
-                        * Get the field name.  Note we assume here that there's only one
-                        * field being assigned to.  This is okay in stored rules but
+                        * Print the field name.  Note we assume here that there's only
+                        * one field being assigned to.  This is okay in stored rules but
                         * could be wrong in executable target lists.  Presently no
                         * problem since explain.c doesn't print plan targetlists, but
                         * someday may have to think of something ...
                         */
                        fieldname = get_relid_attribute_name(typrelid,
                                                                                        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
@@ -4624,7 +4738,8 @@ processIndirection(Node *node, deparse_context *context)
 
                        if (aref->refassgnexpr == NULL)
                                break;
-                       printSubscripts(aref, context);
+                       if (printit)
+                               printSubscripts(aref, context);
 
                        /*
                         * We ignore refexpr since it should be an uninteresting reference
index 23b0ac9..8d93308 100644 (file)
@@ -37,7 +37,7 @@
  * Portions Copyright (c) 1996-2006, PostgreSQL Global Development Group
  * 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 */
-#define CATALOG_VERSION_NO     200607311
+#define CATALOG_VERSION_NO     200608011
 
 #endif
diff --git a/src/include/executor/nodeValuesscan.h b/src/include/executor/nodeValuesscan.h
new file mode 100644 (file)
index 0000000..5a952bd
--- /dev/null
@@ -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 */
index 8dec413..2e98124 100644 (file)
@@ -7,7 +7,7 @@
  * Portions Copyright (c) 1996-2006, PostgreSQL Global Development Group
  * 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;
 } 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
  * ----------------------------------------------------------------
index 53f3ee1..eb31fd2 100644 (file)
@@ -7,7 +7,7 @@
  * Portions Copyright (c) 1996-2006, PostgreSQL Global Development Group
  * 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_SubqueryScan,
        T_FunctionScan,
+       T_ValuesScan,
        T_Join,
        T_NestLoop,
        T_MergeJoin,
@@ -85,6 +86,7 @@ typedef enum NodeTag
        T_TidScanState,
        T_SubqueryScanState,
        T_FunctionScanState,
+       T_ValuesScanState,
        T_JoinState,
        T_NestLoopState,
        T_MergeJoinState,
index 5c22715..d0fa16f 100644 (file)
@@ -7,7 +7,7 @@
  * Portions Copyright (c) 1996-2006, PostgreSQL Global Development Group
  * 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 -
  *       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
  * value expression itself.  The 'indirection' field is not used.
  *
- * INSERT has a second ResTarget list which is the target-column-names list.
- * Here, 'val' is not used, 'name' is the name of the destination column,
- * and 'indirection' stores any subscripts attached to the destination.
+ * INSERT uses ResTarget in its target-column-names list.  Here, 'name' is
+ * the name of the destination column, 'indirection' stores any subscripts
+ * attached to the destination, and 'val' is not used.
  *
  * In an UPDATE target list, 'name' is the name of the destination column,
  * 'indirection' stores any subscripts attached to the destination, and
@@ -517,7 +517,8 @@ typedef enum RTEKind
        RTE_SUBQUERY,                           /* subquery in FROM */
        RTE_JOIN,                                       /* join */
        RTE_SPECIAL,                            /* special rule relation (NEW or OLD) */
-       RTE_FUNCTION                            /* function in FROM */
+       RTE_FUNCTION,                           /* function in FROM */
+       RTE_VALUES                                      /* VALUES (<exprlist>), (<exprlist>), ... */
 } RTEKind;
 
 typedef struct RangeTblEntry
@@ -554,6 +555,11 @@ typedef struct RangeTblEntry
        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):
         *
         * joinaliasvars is a list of Vars or COALESCE expressions corresponding
@@ -630,6 +636,10 @@ typedef struct RowMarkClause
 
 /* ----------------------
  *             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
@@ -637,14 +647,7 @@ typedef struct InsertStmt
        NodeTag         type;
        RangeVar   *relation;           /* relation to insert into */
        List       *cols;                       /* optional: names of the target columns */
-
-       /*
-        * 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 */
+       Node       *selectStmt;         /* the source SELECT/VALUES, or NULL */
 } InsertStmt;
 
 /* ----------------------
@@ -676,9 +679,9 @@ typedef struct UpdateStmt
  *             Select Statement
  *
  * A "simple" SELECT is represented in the output of gram.y by a single
- * SelectStmt node.  A SELECT construct containing set operators (UNION,
- * INTERSECT, EXCEPT) is represented by a tree of SelectStmt nodes, in
- * which the leaf nodes are component SELECTs and the internal nodes
+ * SelectStmt node; so is a VALUES construct.  A query containing set
+ * operators (UNION, INTERSECT, EXCEPT) is represented by a tree of SelectStmt
+ * nodes, in which the leaf nodes are component SELECTs and the internal nodes
  * represent UNION, INTERSECT, or EXCEPT operators.  Using the same node
  * type for both leaf and internal nodes allows gram.y to stick ORDER BY,
  * LIMIT, etc, clause values into a SELECT statement without worrying
@@ -717,6 +720,16 @@ typedef struct SelectStmt
        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
         * SelectStmts.
         */
index 78a4723..5c2de28 100644 (file)
@@ -7,7 +7,7 @@
  * Portions Copyright (c) 1996-2006, PostgreSQL Global Development Group
  * 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 */
 } FunctionScan;
 
+/* ----------------
+ *             ValuesScan node
+ * ----------------
+ */
+typedef struct ValuesScan
+{
+       Scan            scan;
+       /* no other fields needed at present */
+} ValuesScan;
+
 /*
  * ==========
  * Join nodes
index 27928b8..3038857 100644 (file)
@@ -7,7 +7,7 @@
  * Portions Copyright (c) 1996-2006, PostgreSQL Global Development Group
  * 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_functionscan(Path *path, PlannerInfo *root,
                                  RelOptInfo *baserel);
+extern void cost_valuesscan(Path *path, PlannerInfo *root,
+                                 RelOptInfo *baserel);
 extern void cost_sort(Path *path, PlannerInfo *root,
                  List *pathkeys, Cost input_cost, double tuples, int width);
 extern void cost_material(Path *path,
@@ -94,6 +96,7 @@ extern void set_joinrel_size_estimates(PlannerInfo *root, RelOptInfo *rel,
                                                   JoinType jointype,
                                                   List *restrictlist);
 extern void set_function_size_estimates(PlannerInfo *root, RelOptInfo *rel);
+extern void set_values_size_estimates(PlannerInfo *root, RelOptInfo *rel);
 
 /*
  * prototypes for clausesel.c
index 71426b5..e600509 100644 (file)
@@ -7,7 +7,7 @@
  * Portions Copyright (c) 1996-2006, PostgreSQL Global Development Group
  * 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);
 extern Path *create_subqueryscan_path(RelOptInfo *rel, List *pathkeys);
 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,
                                         RelOptInfo *joinrel,
index 33ebad8..9e2df63 100644 (file)
@@ -7,7 +7,7 @@
  * Portions Copyright (c) 1996-2006, PostgreSQL Global Development Group
  * 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,
                                                          RangeFunction *rangefunc,
                                                          bool inFromCl);
+extern RangeTblEntry *addRangeTableEntryForValues(ParseState *pstate,
+                                                       List *exprs,
+                                                       Alias *alias,
+                                                       bool inFromCl);
 extern RangeTblEntry *addRangeTableEntryForJoin(ParseState *pstate,
                                                  List *colnames,
                                                  JoinType jointype,
index 6080696..bbd154e 100644 (file)
@@ -7,7 +7,7 @@
  * Portions Copyright (c) 1996-2006, PostgreSQL Global Development Group
  * 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 $
  *
  *-------------------------------------------------------------------------
  */
 
 
 extern List *transformTargetList(ParseState *pstate, List *targetlist);
+extern List *transformExpressionList(ParseState *pstate, List *exprlist);
 extern void markTargetListOrigins(ParseState *pstate, List *targetlist);
 extern TargetEntry *transformTargetEntry(ParseState *pstate,
                                         Node *node, Node *expr,
                                         char *colname, bool resjunk);
-extern List *ExpandColumnRefStar(ParseState *pstate, ColumnRef *cref,
-                                                                bool targetlist);
-extern List *ExpandIndirectionStar(ParseState *pstate, A_Indirection *ind,
-                                                                  bool targetlist);
+extern Expr *transformAssignedExpr(ParseState *pstate, Expr *expr,
+                                         char *colname,
+                                         int attrno,
+                                         List *indirection,
+                                         int location);
 extern void updateTargetListEntry(ParseState *pstate, TargetEntry *tle,
                                          char *colname, int attrno,
                                          List *indirection,