Index: doc/src/sgml/ref/insert.sgml =================================================================== RCS file: /home/projects/pgsql/cvsroot/pgsql/doc/src/sgml/ref/insert.sgml,v retrieving revision 1.14 diff -c -r1.14 insert.sgml *** doc/src/sgml/ref/insert.sgml 2001/05/27 09:59:28 1.14 --- doc/src/sgml/ref/insert.sgml 2001/08/29 16:36:27 *************** *** 22,28 **** INSERT INTO table [ ( column [, ...] ) ] ! { DEFAULT VALUES | VALUES ( expression [, ...] ) | SELECT query } --- 22,30 ---- INSERT INTO table [ ( column [, ...] ) ] ! { DEFAULT VALUES | ! VALUES ( expression [, ...] ), [ ( expression [, ...] ), ... ] | ! SELECT query } *************** *** 126,135 **** ! INSERT allows one to insert new rows into a ! table. One can insert ! a single row at a time or several rows as a result of a query. ! The columns in the target list may be listed in any order. --- 128,137 ---- ! INSERT allows one to insert new rows into a table. One can ! insert either a single row or several rows. The rows to be inserted may be ! the result of a query. The columns in the target list may be listed in any ! order. *************** *** 182,187 **** --- 184,197 ---- INSERT INTO distributors (name) VALUES ('British Lion'); + + + + + Same as above except insert multiple rows into the table: + + + INSERT INTO distributors (name) VALUES ('Paramount'), ('MGM'), ('Alliance'); Index: src/backend/catalog/pg_proc.c =================================================================== RCS file: /home/projects/pgsql/cvsroot/pgsql/src/backend/catalog/pg_proc.c,v retrieving revision 1.58 diff -c -r1.58 pg_proc.c *** src/backend/catalog/pg_proc.c 2001/08/23 00:49:46 1.58 --- src/backend/catalog/pg_proc.c 2001/08/29 16:36:27 *************** *** 352,358 **** parse = (Query *) nth(length(queryTreeList) - 1, queryTreeList); cmd = parse->commandType; - tlist = parse->targetList; /* * The last query must be a SELECT if and only if there is a return --- 352,357 ---- *************** *** 369,374 **** --- 368,377 ---- if (cmd != CMD_SELECT) elog(ERROR, "function declared to return %s, but final statement is not a SELECT", format_type_be(rettype)); + + /* SELECT so only one target list is allowed */ + Assert(length(parse->targetLists) == 1); + tlist = lfirst(parse->targetLists); /* * Count the non-junk entries in the result targetlist. Index: src/backend/commands/view.c =================================================================== RCS file: /home/projects/pgsql/cvsroot/pgsql/src/backend/commands/view.c,v retrieving revision 1.57 diff -c -r1.57 view.c *** src/backend/commands/view.c 2001/08/16 20:38:53 1.57 --- src/backend/commands/view.c 2001/08/29 16:36:27 *************** *** 230,240 **** DefineView(char *viewName, Query *viewParse) { /* * Create the "view" relation NOTE: if it already exists, the xact * will be aborted. */ ! DefineVirtualRelation(viewName, viewParse->targetList); /* * The relation we have just created is not visible to any other --- 230,243 ---- DefineView(char *viewName, Query *viewParse) { + /* viewParse can only have one targetList */ + Assert(length(viewParse->targetLists) == 1); + /* * Create the "view" relation NOTE: if it already exists, the xact * will be aborted. */ ! DefineVirtualRelation(viewName, lfirst(viewParse->targetLists)); /* * The relation we have just created is not visible to any other Index: src/backend/executor/nodeResult.c =================================================================== RCS file: /home/projects/pgsql/cvsroot/pgsql/src/backend/executor/nodeResult.c,v retrieving revision 1.19 diff -c -r1.19 nodeResult.c *** src/backend/executor/nodeResult.c 2001/03/22 06:16:13 1.19 --- src/backend/executor/nodeResult.c 2001/08/29 16:36:27 *************** *** 148,157 **** { /* ! * if we don't have an outer plan, then we are just generating ! * the results from a constant target list. Do it only once. */ ! resstate->rs_done = true; } /* --- 148,163 ---- { /* ! * if we don't have an outer plan, then we are just ! * generating the results from a constant list of target ! * lists. Do it only while the list has elements in it. ! * An item in the list is placed in the pi_targetlist of ! * resstate->cstate.cs_ProjInfo for processing. */ ! if (resstate->rs_targetlists == NIL) ! return NULL; ! resstate->cstate.cs_ProjInfo->pi_targetlist = lfirst(resstate->rs_targetlists); ! resstate->rs_targetlists = lnext(resstate->rs_targetlists); } /* *************** *** 195,200 **** --- 201,207 ---- resstate = makeNode(ResultState); resstate->rs_done = false; resstate->rs_checkqual = (node->resconstantqual == NULL) ? false : true; + resstate->rs_targetlists = node->targetlists; node->resstate = resstate; /* *************** *** 280,285 **** --- 287,293 ---- resstate->rs_done = false; resstate->cstate.cs_TupFromTlist = false; resstate->rs_checkqual = (node->resconstantqual == NULL) ? false : true; + resstate->rs_targetlists = node->targetlists; /* * if chgParam of subnode is not null then plan will be re-scanned by Index: src/backend/nodes/copyfuncs.c =================================================================== RCS file: /home/projects/pgsql/cvsroot/pgsql/src/backend/nodes/copyfuncs.c,v retrieving revision 1.155 diff -c -r1.155 copyfuncs.c *** src/backend/nodes/copyfuncs.c 2001/08/26 16:55:59 1.155 --- src/backend/nodes/copyfuncs.c 2001/08/29 16:36:27 *************** *** 140,145 **** --- 140,146 ---- * copy remainder of node */ Node_Copy(from, newnode, resconstantqual); + Node_Copy(from, newnode, targetlists); /* * We must add subplans in resconstantqual to the new plan's subPlan *************** *** 1764,1770 **** newnode->rowMarks = listCopy(from->rowMarks); ! Node_Copy(from, newnode, targetList); Node_Copy(from, newnode, groupClause); Node_Copy(from, newnode, havingQual); --- 1765,1771 ---- newnode->rowMarks = listCopy(from->rowMarks); ! Node_Copy(from, newnode, targetLists); Node_Copy(from, newnode, groupClause); Node_Copy(from, newnode, havingQual); *************** *** 1795,1801 **** if (from->relname) newnode->relname = pstrdup(from->relname); Node_Copy(from, newnode, cols); ! Node_Copy(from, newnode, targetList); Node_Copy(from, newnode, selectStmt); return newnode; --- 1796,1802 ---- if (from->relname) newnode->relname = pstrdup(from->relname); Node_Copy(from, newnode, cols); ! Node_Copy(from, newnode, values); Node_Copy(from, newnode, selectStmt); return newnode; Index: src/backend/nodes/equalfuncs.c =================================================================== RCS file: /home/projects/pgsql/cvsroot/pgsql/src/backend/nodes/equalfuncs.c,v retrieving revision 1.103 diff -c -r1.103 equalfuncs.c *** src/backend/nodes/equalfuncs.c 2001/08/26 16:55:59 1.103 --- src/backend/nodes/equalfuncs.c 2001/08/29 16:36:27 *************** *** 601,607 **** return false; if (!equali(a->rowMarks, b->rowMarks)) return false; ! if (!equal(a->targetList, b->targetList)) return false; if (!equal(a->groupClause, b->groupClause)) return false; --- 601,607 ---- return false; if (!equali(a->rowMarks, b->rowMarks)) return false; ! if (!equal(a->targetLists, b->targetLists)) return false; if (!equal(a->groupClause, b->groupClause)) return false; *************** *** 636,642 **** return false; if (!equal(a->cols, b->cols)) return false; ! if (!equal(a->targetList, b->targetList)) return false; if (!equal(a->selectStmt, b->selectStmt)) return false; --- 636,642 ---- return false; if (!equal(a->cols, b->cols)) return false; ! if (!equal(a->values, b->values)) return false; if (!equal(a->selectStmt, b->selectStmt)) return false; Index: src/backend/nodes/outfuncs.c =================================================================== RCS file: /home/projects/pgsql/cvsroot/pgsql/src/backend/nodes/outfuncs.c,v retrieving revision 1.145 diff -c -r1.145 outfuncs.c *** src/backend/nodes/outfuncs.c 2001/08/21 16:36:02 1.145 --- src/backend/nodes/outfuncs.c 2001/08/29 16:36:27 *************** *** 271,278 **** appendStringInfo(str, " :rowMarks "); _outIntList(str, node->rowMarks); ! appendStringInfo(str, " :targetList "); ! _outNode(str, node->targetList); appendStringInfo(str, " :groupClause "); _outNode(str, node->groupClause); --- 271,278 ---- appendStringInfo(str, " :rowMarks "); _outIntList(str, node->rowMarks); ! appendStringInfo(str, " :targetLists "); ! _outNode(str, node->targetLists); appendStringInfo(str, " :groupClause "); _outNode(str, node->groupClause); *************** *** 378,383 **** --- 378,386 ---- { appendStringInfo(str, " RESULT "); _outPlanInfo(str, (Plan *) node); + + appendStringInfo(str, " :targetlists "); + _outNode(str, node->targetlists); appendStringInfo(str, " :resconstantqual "); _outNode(str, node->resconstantqual); Index: src/backend/nodes/readfuncs.c =================================================================== RCS file: /home/projects/pgsql/cvsroot/pgsql/src/backend/nodes/readfuncs.c,v retrieving revision 1.112 diff -c -r1.112 readfuncs.c *** src/backend/nodes/readfuncs.c 2001/07/03 16:52:48 1.112 --- src/backend/nodes/readfuncs.c 2001/08/29 16:36:27 *************** *** 178,185 **** token = pg_strtok(&length); /* skip :rowMarks */ local_node->rowMarks = toIntList(nodeRead(true)); ! token = pg_strtok(&length); /* skip :targetlist */ ! local_node->targetList = nodeRead(true); token = pg_strtok(&length); /* skip :groupClause */ local_node->groupClause = nodeRead(true); --- 178,185 ---- token = pg_strtok(&length); /* skip :rowMarks */ local_node->rowMarks = toIntList(nodeRead(true)); ! token = pg_strtok(&length); /* skip :targetLists */ ! local_node->targetLists = nodeRead(true); token = pg_strtok(&length); /* skip :groupClause */ local_node->groupClause = nodeRead(true); *************** *** 366,371 **** --- 366,374 ---- local_node = makeNode(Result); _getPlan((Plan *) local_node); + + token = pg_strtok(&length); /* eat :targetlists */ + local_node->targetlists = nodeRead(true); /* now read it */ token = pg_strtok(&length); /* eat :resconstantqual */ local_node->resconstantqual = nodeRead(true); /* now read it */ Index: src/backend/optimizer/path/allpaths.c =================================================================== RCS file: /home/projects/pgsql/cvsroot/pgsql/src/backend/optimizer/path/allpaths.c,v retrieving revision 1.78 diff -c -r1.78 allpaths.c *** src/backend/optimizer/path/allpaths.c 2001/07/31 17:56:30 1.78 --- src/backend/optimizer/path/allpaths.c 2001/08/29 16:36:27 *************** *** 277,284 **** --- 277,292 ---- Index rti, RangeTblEntry *rte) { Query *subquery = rte->subquery; + List *tlist; /* + * subqueries can only be SELECTs right now so there should only + * be one target list + */ + Assert(length(subquery->targetLists) == 1); + tlist = lfirst(subquery->targetLists); + + /* * If there are any restriction clauses that have been attached to the * subquery relation, consider pushing them down to become HAVING quals * of the subquery itself. (Not WHERE clauses, since they may refer to *************** *** 342,348 **** * Params, so they need no work. */ clause = ResolveNew(clause, rti, 0, ! subquery->targetList, CMD_SELECT, 0); subquery->havingQual = make_and_qual(subquery->havingQual, clause); --- 350,356 ---- * Params, so they need no work. */ clause = ResolveNew(clause, rti, 0, ! tlist, CMD_SELECT, 0); subquery->havingQual = make_and_qual(subquery->havingQual, clause); Index: src/backend/optimizer/plan/createplan.c =================================================================== RCS file: /home/projects/pgsql/cvsroot/pgsql/src/backend/optimizer/plan/createplan.c,v retrieving revision 1.108 diff -c -r1.108 createplan.c *** src/backend/optimizer/plan/createplan.c 2001/08/21 16:36:03 1.108 --- src/backend/optimizer/plan/createplan.c 2001/08/29 16:36:28 *************** *** 1817,1823 **** } Result * ! make_result(List *tlist, Node *resconstantqual, Plan *subplan) { --- 1817,1823 ---- } Result * ! make_result(List *tlists, Node *resconstantqual, Plan *subplan) { *************** *** 1829,1840 **** #endif copy_plan_costsize(plan, subplan); plan->state = (EState *) NULL; ! plan->targetlist = tlist; plan->qual = NIL; plan->lefttree = subplan; plan->righttree = NULL; node->resconstantqual = resconstantqual; node->resstate = NULL; return node; } --- 1829,1841 ---- #endif copy_plan_costsize(plan, subplan); plan->state = (EState *) NULL; ! plan->targetlist = lfirst(tlists); plan->qual = NIL; plan->lefttree = subplan; plan->righttree = NULL; node->resconstantqual = resconstantqual; node->resstate = NULL; + node->targetlists = tlists; return node; } Index: src/backend/optimizer/plan/planmain.c =================================================================== RCS file: /home/projects/pgsql/cvsroot/pgsql/src/backend/optimizer/plan/planmain.c,v retrieving revision 1.66 diff -c -r1.66 planmain.c *** src/backend/optimizer/plan/planmain.c 2001/06/05 05:26:04 1.66 --- src/backend/optimizer/plan/planmain.c 2001/08/29 16:36:28 *************** *** 89,95 **** root->query_pathkeys = NIL; /* signal unordered result */ /* Make childless Result node to evaluate given tlist. */ ! return (Plan *) make_result(tlist, root->jointree->quals, (Plan *) NULL); } --- 89,95 ---- root->query_pathkeys = NIL; /* signal unordered result */ /* Make childless Result node to evaluate given tlist. */ ! return (Plan *) make_result(root->targetLists, root->jointree->quals, (Plan *) NULL); } *************** *** 142,148 **** * The result node will also be responsible for evaluating the * originally requested tlist. */ ! subplan = (Plan *) make_result(tlist, (Node *) constant_quals, subplan); } --- 142,148 ---- * The result node will also be responsible for evaluating the * originally requested tlist. */ ! subplan = (Plan *) make_result(makeList1(tlist), (Node *) constant_quals, subplan); } Index: src/backend/optimizer/plan/planner.c =================================================================== RCS file: /home/projects/pgsql/cvsroot/pgsql/src/backend/optimizer/plan/planner.c,v retrieving revision 1.108 diff -c -r1.108 planner.c *** src/backend/optimizer/plan/planner.c 2001/06/05 05:26:04 1.108 --- src/backend/optimizer/plan/planner.c 2001/08/29 16:36:28 *************** *** 134,140 **** int saved_planid = PlannerPlanId; Plan *plan; List *newHaving; ! List *lst; /* Set up for a new level of subquery */ PlannerQueryLevel++; --- 134,140 ---- int saved_planid = PlannerPlanId; Plan *plan; List *newHaving; ! List *lst, *tlist, *newlist; /* Set up for a new level of subquery */ PlannerQueryLevel++; *************** *** 161,173 **** /* * Do expression preprocessing on targetlist and quals. */ ! parse->targetList = (List *) ! preprocess_expression(parse, (Node *) parse->targetList, ! EXPRKIND_TARGET); preprocess_qual_conditions(parse, (Node *) parse->jointree); ! parse->havingQual = preprocess_expression(parse, parse->havingQual, EXPRKIND_HAVING); /* --- 161,178 ---- /* * Do expression preprocessing on targetlist and quals. */ ! newlist = NIL; ! foreach(tlist, parse->targetLists) ! { ! List *tl = lfirst(tlist); ! tl = (List *) preprocess_expression(parse, (Node *) tl, EXPRKIND_TARGET); ! newlist = lappend(newlist, tl); ! } ! parse->targetLists = newlist; preprocess_qual_conditions(parse, (Node *) parse->jointree); ! parse->havingQual = preprocess_expression(parse, parse->havingQual, EXPRKIND_HAVING); /* *************** *** 283,290 **** int rtoffset; Node *subjointree; List *subtlist; ! List *l; /* * First, recursively pull up the subquery's subqueries, so * that this routine's processing is complete for its jointree --- 288,300 ---- int rtoffset; Node *subjointree; List *subtlist; ! List *l, *tlist; + if (length(parse->targetLists) == 0) + tlist = NIL; + else + tlist = lfirst(parse->targetLists); + /* * First, recursively pull up the subquery's subqueries, so * that this routine's processing is complete for its jointree *************** *** 313,319 **** */ subjointree = copyObject(subquery->jointree); OffsetVarNodes(subjointree, rtoffset, 0); ! subtlist = copyObject(subquery->targetList); OffsetVarNodes((Node *) subtlist, rtoffset, 0); /* --- 323,329 ---- */ subjointree = copyObject(subquery->jointree); OffsetVarNodes(subjointree, rtoffset, 0); ! subtlist = copyObject(lfirst(subquery->targetLists)); OffsetVarNodes((Node *) subtlist, rtoffset, 0); /* *************** *** 321,333 **** * outputs with copies of the adjusted subtlist items, being * careful not to replace any of the jointree structure. */ ! parse->targetList = (List *) ! ResolveNew((Node *) parse->targetList, ! varno, 0, subtlist, CMD_SELECT, 0); resolvenew_in_jointree((Node *) parse->jointree, varno, subtlist); ! parse->havingQual = ! ResolveNew(parse->havingQual, ! varno, 0, subtlist, CMD_SELECT, 0); /* * Pull up any FOR UPDATE markers, too. --- 331,341 ---- * outputs with copies of the adjusted subtlist items, being * careful not to replace any of the jointree structure. */ ! parse->targetLists = makeList1((List *) ResolveNew((Node *) tlist, ! varno, 0, subtlist, CMD_SELECT, 0)); resolvenew_in_jointree((Node *) parse->jointree, varno, subtlist); ! parse->havingQual = ResolveNew(parse->havingQual, ! varno, 0, subtlist, CMD_SELECT, 0); /* * Pull up any FOR UPDATE markers, too. *************** *** 723,730 **** /* Generate modified query with this rel as target */ subquery = (Query *) adjust_inherited_attrs((Node *) parse, ! parentRTindex, parentOID, ! childRTindex, childOID); /* Generate plan */ subplan = grouping_planner(subquery, 0.0 /* retrieve all tuples */ ); subplans = lappend(subplans, subplan); --- 731,738 ---- /* Generate modified query with this rel as target */ subquery = (Query *) adjust_inherited_attrs((Node *) parse, ! parentRTindex, parentOID, ! childRTindex, childOID); /* Generate plan */ subplan = grouping_planner(subquery, 0.0 /* retrieve all tuples */ ); subplans = lappend(subplans, subplan); *************** *** 764,776 **** static Plan * grouping_planner(Query *parse, double tuple_fraction) { ! List *tlist = parse->targetList; Plan *result_plan; List *current_pathkeys; List *group_pathkeys; List *sort_pathkeys; AttrNumber *groupColIdx = NULL; if (parse->setOperations) { --- 772,789 ---- static Plan * grouping_planner(Query *parse, double tuple_fraction) { ! List *tlist; Plan *result_plan; List *current_pathkeys; List *group_pathkeys; List *sort_pathkeys; AttrNumber *groupColIdx = NULL; + if (parse->targetLists == NIL) + tlist = NIL; + else + tlist = lfirst(parse->targetLists); /* arbitrary choice */ + if (parse->setOperations) { *************** *** 788,793 **** --- 801,807 ---- * information from the original tlist. */ Assert(parse->commandType == CMD_SELECT); + Assert(length(parse->targetLists) == 1); tlist = postprocess_setop_tlist(result_plan->targetlist, tlist); *************** *** 819,831 **** else { List *sub_tlist; ! ! /* Preprocess targetlist in case we are inside an INSERT/UPDATE. */ ! tlist = preprocess_targetlist(tlist, ! parse->commandType, ! parse->resultRelation, ! parse->rtable); /* * Add TID targets for rels selected FOR UPDATE (should this be * done in preprocess_targetlist?). The executor uses the TID to --- 833,858 ---- else { List *sub_tlist; ! List *newlist = NIL, *list; + /* Process targetLists. We need to pre-process one target list though */ + foreach(list, parse->targetLists) + { + List *tl = lfirst(list); + tl = preprocess_targetlist(tl, + parse->commandType, + parse->resultRelation, + parse->rtable); + newlist = lappend(newlist, tl); + } + parse->targetLists = newlist; + if (parse->targetLists == NIL) + parse->targetLists = makeList1(preprocess_targetlist(parse->targetLists, + parse->commandType, + parse->resultRelation, + parse->rtable)); + tlist = lfirst(parse->targetLists); + /* * Add TID targets for rels selected FOR UPDATE (should this be * done in preprocess_targetlist?). The executor uses the TID to *************** *** 834,839 **** --- 861,869 ---- if (parse->rowMarks) { List *l; + + /* doing SELECT FOR UPDATE so only one targetlist allowed */ + Assert(length(parse->targetLists) == 1); /* * We've got trouble if the FOR UPDATE appears inside Index: src/backend/optimizer/prep/prepunion.c =================================================================== RCS file: /home/projects/pgsql/cvsroot/pgsql/src/backend/optimizer/prep/prepunion.c,v retrieving revision 1.66 diff -c -r1.66 prepunion.c *** src/backend/optimizer/prep/prepunion.c 2001/08/14 17:12:57 1.66 --- src/backend/optimizer/prep/prepunion.c 2001/08/29 16:36:28 *************** *** 97,102 **** --- 97,103 ---- leftmostQuery = rt_fetch(((RangeTblRef *) node)->rtindex, parse->rtable)->subquery; Assert(leftmostQuery != NULL); + Assert(length(leftmostQuery->targetLists) == 1); /* * Recurse on setOperations tree to generate plans for set ops. The *************** *** 106,112 **** */ return recurse_set_operations((Node *) topop, parse, topop->colTypes, true, -1, ! leftmostQuery->targetList); } /* --- 107,113 ---- */ return recurse_set_operations((Node *) topop, parse, topop->colTypes, true, -1, ! lfirst(leftmostQuery->targetLists)); } /* *************** *** 180,188 **** !tlist_same_datatypes(plan->targetlist, colTypes, junkOK)) { plan = (Plan *) ! make_result(generate_setop_tlist(colTypes, flag, false, ! plan->targetlist, ! refnames_tlist), NULL, plan); } --- 181,189 ---- !tlist_same_datatypes(plan->targetlist, colTypes, junkOK)) { plan = (Plan *) ! make_result(makeList1(generate_setop_tlist(colTypes, flag, false, ! plan->targetlist, ! refnames_tlist)), NULL, plan); } Index: src/backend/optimizer/util/clauses.c =================================================================== RCS file: /home/projects/pgsql/cvsroot/pgsql/src/backend/optimizer/util/clauses.c,v retrieving revision 1.88 diff -c -r1.88 clauses.c *** src/backend/optimizer/util/clauses.c 2001/07/31 20:16:33 1.88 --- src/backend/optimizer/util/clauses.c 2001/08/29 16:36:28 *************** *** 538,546 **** --- 538,551 ---- check_subplans_for_ungrouped_vars_walker(Node *node, Query *context) { + List *tlist; + if (node == NULL) return false; + Assert(length(context->targetLists) == 1); + tlist = lfirst(context->targetLists); + /* * If we find an aggregate function, do not recurse into its * arguments. Subplans invoked within aggregate calls are allowed to *************** *** 590,597 **** GroupClause *gcl = lfirst(gl); Node *groupexpr; ! groupexpr = get_sortgroupclause_expr(gcl, ! context->targetList); if (equal(thisarg, groupexpr)) { contained_in_group_clause = true; --- 595,601 ---- GroupClause *gcl = lfirst(gl); Node *groupexpr; ! groupexpr = get_sortgroupclause_expr(gcl, tlist); if (equal(thisarg, groupexpr)) { contained_in_group_clause = true; *************** *** 765,775 **** bool has_distinct_on_clause(Query *query) { ! List *targetList; /* Is there a DISTINCT clause at all? */ if (query->distinctClause == NIL) return false; /* * If the DISTINCT list contains all the nonjunk targetlist items, * and nothing else (ie, no junk tlist items), then it's a simple --- 769,783 ---- bool has_distinct_on_clause(Query *query) { ! List *targetList, *tlist; /* Is there a DISTINCT clause at all? */ if (query->distinctClause == NIL) return false; + + Assert(length(query->targetLists) == 1); + tlist = lfirst(query->targetLists); + /* * If the DISTINCT list contains all the nonjunk targetlist items, * and nothing else (ie, no junk tlist items), then it's a simple *************** *** 783,789 **** * This code assumes that the DISTINCT list is valid, ie, all its entries * match some entry of the tlist. */ ! foreach(targetList, query->targetList) { TargetEntry *tle = (TargetEntry *) lfirst(targetList); Index ressortgroupref = tle->resdom->ressortgroupref; --- 791,797 ---- * This code assumes that the DISTINCT list is valid, ie, all its entries * match some entry of the tlist. */ ! foreach(targetList, tlist) { TargetEntry *tle = (TargetEntry *) lfirst(targetList); Index ressortgroupref = tle->resdom->ressortgroupref; *************** *** 1780,1789 **** void *context, bool visitQueryRTEs) { Assert(query != NULL && IsA(query, Query)); ! if (walker((Node *) query->targetList, context)) ! return true; if (walker((Node *) query->jointree, context)) return true; if (walker(query->setOperations, context)) --- 1788,1802 ---- void *context, bool visitQueryRTEs) { + List *rt; Assert(query != NULL && IsA(query, Query)); ! foreach(rt, query->targetLists) ! { ! List *l = lfirst(rt); ! if (walker((Node *) l, context)) ! return true; ! } if (walker((Node *) query->jointree, context)) return true; if (walker(query->setOperations, context)) *************** *** 1792,1799 **** return true; if (visitQueryRTEs) { - List *rt; - foreach(rt, query->rtable) { RangeTblEntry *rte = (RangeTblEntry *) lfirst(rt); --- 1805,1810 ---- *************** *** 2168,2174 **** { Assert(query != NULL && IsA(query, Query)); ! MUTATE(query->targetList, query->targetList, List *); MUTATE(query->jointree, query->jointree, FromExpr *); MUTATE(query->setOperations, query->setOperations, Node *); MUTATE(query->havingQual, query->havingQual, Node *); --- 2179,2185 ---- { Assert(query != NULL && IsA(query, Query)); ! MUTATE(query->targetLists, query->targetLists, List *); MUTATE(query->jointree, query->jointree, FromExpr *); MUTATE(query->setOperations, query->setOperations, Node *); MUTATE(query->havingQual, query->havingQual, Node *); Index: src/backend/parser/analyze.c =================================================================== RCS file: /home/projects/pgsql/cvsroot/pgsql/src/backend/parser/analyze.c,v retrieving revision 1.197 diff -c -r1.197 analyze.c *** src/backend/parser/analyze.c 2001/08/24 20:03:45 1.197 --- src/backend/parser/analyze.c 2001/08/29 16:36:28 *************** *** 66,71 **** --- 66,73 ---- static void release_pstate_resources(ParseState *pstate); static FromExpr *makeFromExpr(List *fromlist, Node *quals); + static List *prepareInsertTargetList(ParseState *pstate, InsertStmt *stmt, List *list); + /* kluge to return extra info from transformCreateStmt() */ static List *extras_before; static List *extras_after; *************** *** 170,176 **** List *aliaslist = n->aliases; List *targetList; ! foreach(targetList, n->query->targetList) { TargetEntry *te = (TargetEntry *) lfirst(targetList); Resdom *rd; --- 172,180 ---- List *aliaslist = n->aliases; List *targetList; ! /* assume only one targetlist */ ! Assert(length(n->query->targetLists) == 1); ! foreach(targetList, lfirst(n->query->targetLists)) { TargetEntry *te = (TargetEntry *) lfirst(targetList); Resdom *rd; *************** *** 294,299 **** --- 298,305 ---- if (pstate->p_hasAggs) parseCheckAggregates(pstate, qry, qual); + qry->targetLists = makeList1(NIL); + return qry; } *************** *** 307,318 **** Query *qry = makeNode(Query); List *sub_rtable; List *sub_namespace; - List *icolumns; - List *attrnos; - List *attnos; - int numuseratts; List *tl; ! TupleDesc rd_att; qry->commandType = CMD_INSERT; pstate->p_is_insert = true; --- 313,320 ---- Query *qry = makeNode(Query); List *sub_rtable; List *sub_namespace; List *tl; ! List *list, *newlist; qry->commandType = CMD_INSERT; pstate->p_is_insert = true; *************** *** 406,413 **** * constants. Otherwise this fails: INSERT INTO foo SELECT 'bar', * ... FROM baz */ ! qry->targetList = NIL; ! foreach(tl, selectQuery->targetList) { TargetEntry *tle = (TargetEntry *) lfirst(tl); Resdom *resnode = tle->resdom; --- 408,416 ---- * constants. Otherwise this fails: INSERT INTO foo SELECT 'bar', * ... FROM baz */ ! newlist = NIL; ! Assert(length(selectQuery->targetLists) == 1); /* only 1 targetlist allowed */ ! foreach(tl, lfirst(selectQuery->targetLists)) { TargetEntry *tle = (TargetEntry *) lfirst(tl); Resdom *resnode = tle->resdom; *************** *** 425,443 **** 0); resnode = copyObject(resnode); resnode->resno = (AttrNumber) pstate->p_last_resno++; ! qry->targetList = lappend(qry->targetList, ! makeTargetEntry(resnode, expr)); } } ! else { ! /* ! * For INSERT ... VALUES, transform the given list of values to ! * form a targetlist for the INSERT. */ ! qry->targetList = transformTargetList(pstate, stmt->targetList); } /* * Now we are done with SELECT-like processing, and can get on with --- 428,456 ---- 0); resnode = copyObject(resnode); resnode->resno = (AttrNumber) pstate->p_last_resno++; ! newlist = lappend(newlist, ! makeTargetEntry(resnode, expr)); } + qry->targetLists = makeList1(newlist); } ! else if (stmt->values) { ! /* ! * No select, so we have a VALUES clause. Transform all lists ! * in values to create qry->targetLists. */ ! List *list; ! ! qry->targetLists = NIL; ! foreach(list, stmt->values) ! { ! qry->targetLists = lappend(qry->targetLists, ! transformTargetList(pstate, lfirst(list))); ! } } + else + qry->targetLists = makeList1(transformTargetList(pstate, NIL)); /* * Now we are done with SELECT-like processing, and can get on with *************** *** 448,453 **** --- 461,494 ---- if (pstate->p_last_resno <= pstate->p_target_relation->rd_rel->relnatts) pstate->p_last_resno = pstate->p_target_relation->rd_rel->relnatts + 1; + /* Prepare qry->targetLists */ + newlist = NIL; + foreach(list, qry->targetLists) + { + List *tl = (List *) lfirst(list); + newlist = lappend(newlist, prepareInsertTargetList(pstate, stmt, tl)); + } + qry->targetLists = newlist; + + /* 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; + if (pstate->p_hasAggs) + parseCheckAggregates(pstate, qry, NULL); + + return qry; + } + + static List * + prepareInsertTargetList(ParseState *pstate, InsertStmt *stmt, List *list) + { + List *attrnos, *attnos, *tl, *icolumns; + int numuseratts; + TupleDesc rd_att; + /* Validate stmt->cols list, or build default list if no list given */ icolumns = checkInsertTargets(pstate, stmt->cols, &attrnos); *************** *** 456,462 **** */ numuseratts = 0; attnos = attrnos; ! foreach(tl, qry->targetList) { TargetEntry *tle = (TargetEntry *) lfirst(tl); Ident *id; --- 497,503 ---- */ numuseratts = 0; attnos = attrnos; ! foreach(tl, list) { TargetEntry *tle = (TargetEntry *) lfirst(tl); Ident *id; *************** *** 511,523 **** * expr and correct data for the target column. */ te = makeTargetEntry( ! makeResdom(attrno, ! thisatt->atttypid, ! thisatt->atttypmod, ! pstrdup(NameStr(thisatt->attname)), ! false), ! stringToNode(defval[ndef].adbin)); ! qry->targetList = lappend(qry->targetList, te); /* * Make sure the value is coerced to the target column type --- 552,564 ---- * expr and correct data for the target column. */ te = makeTargetEntry( ! makeResdom(attrno, ! thisatt->atttypid, ! thisatt->atttypmod, ! pstrdup(NameStr(thisatt->attname)), ! false), ! stringToNode(defval[ndef].adbin)); ! list = lappend(list, te); /* * Make sure the value is coerced to the target column type *************** *** 528,543 **** } } ! /* 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; ! if (pstate->p_hasAggs) ! parseCheckAggregates(pstate, qry, NULL); ! ! return qry; } /* --- 569,575 ---- } } ! return list; } /* *************** *** 1962,1969 **** /* process the FROM clause */ transformFromClause(pstate, stmt->fromClause); ! /* transform targetlist and WHERE */ ! qry->targetList = transformTargetList(pstate, stmt->targetList); qual = transformWhereClause(pstate, stmt->whereClause); --- 1994,2001 ---- /* process the FROM clause */ transformFromClause(pstate, stmt->fromClause); ! /* transform targetlist and WHERE; only one targetlist allowed */ ! qry->targetLists = makeList1(transformTargetList(pstate, stmt->targetList)); qual = transformWhereClause(pstate, stmt->whereClause); *************** *** 1975,1989 **** qry->groupClause = transformGroupClause(pstate, stmt->groupClause, ! qry->targetList); qry->sortClause = transformSortClause(pstate, stmt->sortClause, ! qry->targetList); qry->distinctClause = transformDistinctClause(pstate, stmt->distinctClause, ! qry->targetList, &qry->sortClause); qry->limitOffset = stmt->limitOffset; --- 2007,2021 ---- qry->groupClause = transformGroupClause(pstate, stmt->groupClause, ! lfirst(qry->targetLists)); qry->sortClause = transformSortClause(pstate, stmt->sortClause, ! lfirst(qry->targetLists)); qry->distinctClause = transformDistinctClause(pstate, stmt->distinctClause, ! lfirst(qry->targetLists), &qry->sortClause); qry->limitOffset = stmt->limitOffset; *************** *** 2031,2038 **** --- 2063,2072 ---- Node *node; List *lefttl, *dtlist, + *newlist, *targetvars, *targetnames, + *targetlist, *sv_namespace; JoinExpr *jnode; int tllen; *************** *** 2102,2111 **** * make lists of the dummy vars and their names for use in parsing * ORDER BY. */ - qry->targetList = NIL; targetvars = NIL; targetnames = NIL; ! lefttl = leftmostQuery->targetList; foreach(dtlist, sostmt->colTypes) { Oid colType = (Oid) lfirsti(dtlist); --- 2136,2149 ---- * make lists of the dummy vars and their names for use in parsing * ORDER BY. */ targetvars = NIL; targetnames = NIL; ! newlist = NIL; ! ! /* select can only have one targetlist */ ! Assert(length(leftmostQuery->targetLists) == 1); ! lefttl = lfirst(leftmostQuery->targetLists); ! foreach(dtlist, sostmt->colTypes) { Oid colType = (Oid) lfirsti(dtlist); *************** *** 2124,2135 **** colType, -1, 0); ! qry->targetList = lappend(qry->targetList, ! makeTargetEntry(resdom, expr)); targetvars = lappend(targetvars, expr); targetnames = lappend(targetnames, makeString(colName)); lefttl = lnext(lefttl); } /* * Insert one-time items into top-level query --- 2162,2175 ---- colType, -1, 0); ! newlist = lappend(newlist, ! makeTargetEntry(resdom, expr)); targetvars = lappend(targetvars, expr); targetnames = lappend(targetnames, makeString(colName)); lefttl = lnext(lefttl); } + targetlist = newlist; + qry->targetLists = makeList1(targetlist); /* * Insert one-time items into top-level query *************** *** 2192,2206 **** * selecting an output column by name or number. Enforce by checking * that transformSortClause doesn't add any items to tlist. */ ! tllen = length(qry->targetList); qry->sortClause = transformSortClause(pstate, sortClause, ! qry->targetList); pstate->p_namespace = sv_namespace; ! if (tllen != length(qry->targetList)) elog(ERROR, "ORDER BY on a UNION/INTERSECT/EXCEPT result must be on one of the result columns"); qry->limitOffset = limitOffset; --- 2232,2246 ---- * selecting an output column by name or number. Enforce by checking * that transformSortClause doesn't add any items to tlist. */ ! tllen = length(targetlist); qry->sortClause = transformSortClause(pstate, sortClause, ! targetlist); pstate->p_namespace = sv_namespace; ! if (tllen != length(targetlist)) elog(ERROR, "ORDER BY on a UNION/INTERSECT/EXCEPT result must be on one of the result columns"); qry->limitOffset = limitOffset; *************** *** 2368,2375 **** List *tl; Assert(selectQuery != NULL); /* Get types of non-junk columns */ ! foreach(tl, selectQuery->targetList) { TargetEntry *tle = (TargetEntry *) lfirst(tl); Resdom *resnode = tle->resdom; --- 2408,2416 ---- List *tl; Assert(selectQuery != NULL); + Assert(length(selectQuery->targetLists) == 1); /* Get types of non-junk columns */ ! foreach(tl, lfirst(selectQuery->targetLists)) { TargetEntry *tle = (TargetEntry *) lfirst(tl); Resdom *resnode = tle->resdom; *************** *** 2408,2414 **** Query *qry = makeNode(Query); Node *qual; List *origTargetList; ! List *tl; qry->commandType = CMD_UPDATE; pstate->p_is_update = true; --- 2449,2455 ---- Query *qry = makeNode(Query); Node *qual; List *origTargetList; ! List *tl, *tlist; qry->commandType = CMD_UPDATE; pstate->p_is_update = true; *************** *** 2423,2429 **** */ transformFromClause(pstate, stmt->fromClause); ! qry->targetList = transformTargetList(pstate, stmt->targetList); qual = transformWhereClause(pstate, stmt->whereClause); --- 2464,2472 ---- */ transformFromClause(pstate, stmt->fromClause); ! /* only one target list allowed */ ! tlist = transformTargetList(pstate, stmt->targetList); ! qry->targetLists = makeList1(tlist); qual = transformWhereClause(pstate, stmt->whereClause); *************** *** 2446,2452 **** /* Prepare non-junk columns for assignment to target table */ origTargetList = stmt->targetList; ! foreach(tl, qry->targetList) { TargetEntry *tle = (TargetEntry *) lfirst(tl); Resdom *resnode = tle->resdom; --- 2489,2495 ---- /* Prepare non-junk columns for assignment to target table */ origTargetList = stmt->targetList; ! foreach(tl, tlist) { TargetEntry *tle = (TargetEntry *) lfirst(tl); Resdom *resnode = tle->resdom; Index: src/backend/parser/gram.y =================================================================== RCS file: /home/projects/pgsql/cvsroot/pgsql/src/backend/parser/gram.y,v retrieving revision 2.249 diff -c -r2.249 gram.y *** src/backend/parser/gram.y 2001/08/26 16:55:59 2.249 --- src/backend/parser/gram.y 2001/08/29 16:36:29 *************** *** 194,200 **** from_clause, from_list, opt_array_bounds, expr_list, attrs, target_list, update_target_list, def_list, opt_indirection, group_clause, TriggerFuncArgs, ! select_limit, opt_select_limit %type func_arg, func_return, func_type, aggr_argtype --- 194,200 ---- from_clause, from_list, opt_array_bounds, expr_list, attrs, target_list, update_target_list, def_list, opt_indirection, group_clause, TriggerFuncArgs, ! select_limit, opt_select_limit, values_list %type func_arg, func_return, func_type, aggr_argtype *************** *** 3220,3261 **** } ; ! insert_rest: VALUES '(' target_list ')' { $$ = makeNode(InsertStmt); $$->cols = NIL; ! $$->targetList = $3; $$->selectStmt = NULL; } | DEFAULT VALUES { $$ = makeNode(InsertStmt); $$->cols = NIL; ! $$->targetList = NIL; $$->selectStmt = NULL; } | SelectStmt { $$ = makeNode(InsertStmt); $$->cols = NIL; ! $$->targetList = NIL; $$->selectStmt = $1; } ! | '(' columnList ')' VALUES '(' target_list ')' { $$ = makeNode(InsertStmt); $$->cols = $2; ! $$->targetList = $6; $$->selectStmt = NULL; } | '(' columnList ')' SelectStmt { $$ = makeNode(InsertStmt); $$->cols = $2; ! $$->targetList = NIL; $$->selectStmt = $4; } ; opt_column_list: '(' columnList ')' { $$ = $2; } | /*EMPTY*/ { $$ = NIL; } --- 3220,3267 ---- } ; ! insert_rest: VALUES values_list { $$ = makeNode(InsertStmt); $$->cols = NIL; ! $$->values = $2; $$->selectStmt = NULL; } | DEFAULT VALUES { $$ = makeNode(InsertStmt); $$->cols = NIL; ! $$->values = NIL; $$->selectStmt = NULL; } | SelectStmt { $$ = makeNode(InsertStmt); $$->cols = NIL; ! $$->values = NIL; $$->selectStmt = $1; } ! | '(' columnList ')' VALUES values_list { $$ = makeNode(InsertStmt); $$->cols = $2; ! $$->values = $5; $$->selectStmt = NULL; } | '(' columnList ')' SelectStmt { $$ = makeNode(InsertStmt); $$->cols = $2; ! $$->values = NIL; $$->selectStmt = $4; } ; + + values_list: values_list ',' '(' target_list ')' + { $$ = lappend($1, $4); } + | '(' target_list ')' + { $$ = makeList1($2); } + ; opt_column_list: '(' columnList ')' { $$ = $2; } | /*EMPTY*/ { $$ = NIL; } Index: src/backend/parser/parse_agg.c =================================================================== RCS file: /home/projects/pgsql/cvsroot/pgsql/src/backend/parser/parse_agg.c,v retrieving revision 1.45 diff -c -r1.45 parse_agg.c *** src/backend/parser/parse_agg.c 2001/08/09 18:28:17 1.45 --- src/backend/parser/parse_agg.c 2001/08/29 16:36:29 *************** *** 140,149 **** --- 140,154 ---- { List *groupClauses = NIL; List *tl; + List *tlist; /* This should only be called if we found aggregates, GROUP, or HAVING */ Assert(pstate->p_hasAggs || qry->groupClause || qry->havingQual); + /* There should only be one target list */ + Assert(length(qry->targetLists) == 1); + tlist = lfirst(qry->targetLists); + /* * Aggregates must never appear in WHERE or JOIN/ON clauses. * *************** *** 169,175 **** GroupClause *grpcl = lfirst(tl); Node *expr; ! expr = get_sortgroupclause_expr(grpcl, qry->targetList); if (contain_agg_clause(expr)) elog(ERROR, "Aggregates not allowed in GROUP BY clause"); groupClauses = lcons(expr, groupClauses); --- 174,180 ---- GroupClause *grpcl = lfirst(tl); Node *expr; ! expr = get_sortgroupclause_expr(grpcl, tlist); if (contain_agg_clause(expr)) elog(ERROR, "Aggregates not allowed in GROUP BY clause"); groupClauses = lcons(expr, groupClauses); *************** *** 178,184 **** /* * Check the targetlist and HAVING clause for ungrouped variables. */ ! check_ungrouped_columns((Node *) qry->targetList, pstate, groupClauses); check_ungrouped_columns((Node *) qry->havingQual, pstate, groupClauses); /* Release the list storage (but not the pointed-to expressions!) */ --- 183,189 ---- /* * Check the targetlist and HAVING clause for ungrouped variables. */ ! check_ungrouped_columns((Node *) tlist, pstate, groupClauses); check_ungrouped_columns((Node *) qry->havingQual, pstate, groupClauses); /* Release the list storage (but not the pointed-to expressions!) */ Index: src/backend/parser/parse_expr.c =================================================================== RCS file: /home/projects/pgsql/cvsroot/pgsql/src/backend/parser/parse_expr.c,v retrieving revision 1.99 diff -c -r1.99 parse_expr.c *** src/backend/parser/parse_expr.c 2001/08/09 18:28:17 1.99 --- src/backend/parser/parse_expr.c 2001/08/29 16:36:29 *************** *** 266,272 **** case T_SubLink: { SubLink *sublink = (SubLink *) expr; ! List *qtrees; Query *qtree; /* If we already transformed this node, do nothing */ --- 266,272 ---- case T_SubLink: { SubLink *sublink = (SubLink *) expr; ! List *qtrees, *tlist; Query *qtree; /* If we already transformed this node, do nothing */ *************** *** 285,290 **** --- 285,294 ---- elog(ERROR, "Bad query in subselect"); sublink->subselect = (Node *) qtree; + /* Is this assertion alright? */ + Assert(length(qtree->targetLists) == 1); + tlist = lfirst(qtree->targetLists); + if (sublink->subLinkType == EXISTS_SUBLINK) { *************** *** 297,304 **** } else if (sublink->subLinkType == EXPR_SUBLINK) { - List *tlist = qtree->targetList; - /* * Make sure the subselect delivers a single column * (ignoring resjunk targets). --- 301,306 ---- *************** *** 323,329 **** { /* ALL, ANY, or MULTIEXPR: generate operator list */ List *left_list = sublink->lefthand; ! List *right_list = qtree->targetList; char *op; List *elist; --- 325,331 ---- { /* ALL, ANY, or MULTIEXPR: generate operator list */ List *left_list = sublink->lefthand; ! List *right_list = tlist; char *op; List *elist; *************** *** 686,695 **** /* get the type of the subselect's first target column */ Query *qtree = (Query *) sublink->subselect; TargetEntry *tent; if (!qtree || !IsA(qtree, Query)) elog(ERROR, "Cannot get type for untransformed sublink"); ! tent = (TargetEntry *) lfirst(qtree->targetList); type = tent->resdom->restype; } else --- 688,702 ---- /* get the type of the subselect's first target column */ Query *qtree = (Query *) sublink->subselect; TargetEntry *tent; + List *tlist; if (!qtree || !IsA(qtree, Query)) elog(ERROR, "Cannot get type for untransformed sublink"); ! ! /* is this assertion alright? */ ! Assert(length(qtree->targetLists) == 1); ! tlist = lfirst(qtree->targetLists); ! tent = (TargetEntry *) lfirst(tlist); type = tent->resdom->restype; } else Index: src/backend/parser/parse_node.c =================================================================== RCS file: /home/projects/pgsql/cvsroot/pgsql/src/backend/parser/parse_node.c,v retrieving revision 1.55 diff -c -r1.55 parse_node.c *** src/backend/parser/parse_node.c 2001/08/09 18:28:18 1.55 --- src/backend/parser/parse_node.c 2001/08/29 16:36:29 *************** *** 192,200 **** else { /* Subselect RTE --- get type info from subselect's tlist */ ! List *tlistitem; ! foreach(tlistitem, rte->subquery->targetList) { TargetEntry *te = (TargetEntry *) lfirst(tlistitem); --- 192,204 ---- else { /* Subselect RTE --- get type info from subselect's tlist */ ! List *tlistitem, *tlist; ! /* Subquery should have only one target list */ ! Assert(length(rte->subquery->targetLists) == 1); ! tlist = lfirst(rte->subquery->targetLists); ! ! foreach(tlistitem, tlist) { TargetEntry *te = (TargetEntry *) lfirst(tlistitem); Index: src/backend/parser/parse_relation.c =================================================================== RCS file: /home/projects/pgsql/cvsroot/pgsql/src/backend/parser/parse_relation.c,v retrieving revision 1.56 diff -c -r1.56 parse_relation.c *** src/backend/parser/parse_relation.c 2001/08/10 18:57:37 1.56 --- src/backend/parser/parse_relation.c 2001/08/29 16:36:29 *************** *** 597,603 **** Attr *eref; int numaliases; int varattno; ! List *tlistitem; rte->relname = NULL; rte->relid = InvalidOid; --- 597,603 ---- Attr *eref; int numaliases; int varattno; ! List *tlistitem, *tlist; rte->relname = NULL; rte->relid = InvalidOid; *************** *** 607,615 **** eref = copyObject(alias); numaliases = length(eref->attrs); /* fill in any unspecified alias columns */ varattno = 0; ! foreach(tlistitem, subquery->targetList) { TargetEntry *te = (TargetEntry *) lfirst(tlistitem); --- 607,619 ---- eref = copyObject(alias); numaliases = length(eref->attrs); + /* subquery can only have one targetlist */ + Assert(length(subquery->targetLists) == 1); + tlist = lfirst(subquery->targetLists); + /* fill in any unspecified alias columns */ varattno = 0; ! foreach(tlistitem, tlist) { TargetEntry *te = (TargetEntry *) lfirst(tlistitem); *************** *** 804,813 **** { /* Subquery RTE */ List *aliasp = rte->eref->attrs; ! List *tlistitem; varattno = 0; ! foreach(tlistitem, rte->subquery->targetList) { TargetEntry *te = (TargetEntry *) lfirst(tlistitem); --- 808,821 ---- { /* Subquery RTE */ List *aliasp = rte->eref->attrs; ! List *tlistitem, *tlist; + /* Subquery can only have one targetlist */ + Assert(length(rte->subquery->targetLists) == 1); + tlist = lfirst(rte->subquery->targetLists); + varattno = 0; ! foreach(tlistitem, tlist) { TargetEntry *te = (TargetEntry *) lfirst(tlistitem); Index: src/backend/rewrite/rewriteDefine.c =================================================================== RCS file: /home/projects/pgsql/cvsroot/pgsql/src/backend/rewrite/rewriteDefine.c,v retrieving revision 1.63 diff -c -r1.63 rewriteDefine.c *** src/backend/rewrite/rewriteDefine.c 2001/08/12 21:35:18 1.63 --- src/backend/rewrite/rewriteDefine.c 2001/08/29 16:36:29 *************** *** 260,270 **** elog(ERROR, "event qualifications not supported for rules on select"); /* * ... the targetlist of the SELECT action must exactly match the * event relation, ... */ i = 0; ! foreach(tllist, query->targetList) { TargetEntry *tle = (TargetEntry *) lfirst(tllist); Resdom *resdom = tle->resdom; --- 260,274 ---- elog(ERROR, "event qualifications not supported for rules on select"); /* + * ... the SELECT can only have one target list ... */ + Assert(length(query->targetLists) == 1); + + /* * ... the targetlist of the SELECT action must exactly match the * event relation, ... */ i = 0; ! foreach(tllist, lfirst(query->targetLists)) { TargetEntry *tle = (TargetEntry *) lfirst(tllist); Resdom *resdom = tle->resdom; Index: src/backend/rewrite/rewriteHandler.c =================================================================== RCS file: /home/projects/pgsql/cvsroot/pgsql/src/backend/rewrite/rewriteHandler.c,v retrieving revision 1.97 diff -c -r1.97 rewriteHandler.c *** src/backend/rewrite/rewriteHandler.c 2001/07/09 23:50:32 1.97 --- src/backend/rewrite/rewriteHandler.c 2001/08/29 16:36:29 *************** *** 169,175 **** sub_action = (Query *) ResolveNew((Node *) sub_action, new_varno, 0, ! parsetree->targetList, event, current_varno); if (sub_action_ptr) --- 169,175 ---- sub_action = (Query *) ResolveNew((Node *) sub_action, new_varno, 0, ! lfirst(parsetree->targetLists), event, current_varno); if (sub_action_ptr) *************** *** 621,627 **** new_qual = ResolveNew(new_qual, PRS2_NEW_VARNO, 0, ! parsetree->targetList, event, rt_index); /* And attach the fixed qual */ --- 621,627 ---- new_qual = ResolveNew(new_qual, PRS2_NEW_VARNO, 0, ! lfirst(parsetree->targetLists), event, rt_index); /* And attach the fixed qual */ *************** *** 966,971 **** results = lappend(results, query); } ! return results; } --- 966,971 ---- results = lappend(results, query); } ! return results; } Index: src/backend/rewrite/rewriteManip.c =================================================================== RCS file: /home/projects/pgsql/cvsroot/pgsql/src/backend/rewrite/rewriteManip.c,v retrieving revision 1.57 diff -c -r1.57 rewriteManip.c *** src/backend/rewrite/rewriteManip.c 2001/04/18 20:42:55 1.57 --- src/backend/rewrite/rewriteManip.c 2001/08/29 16:36:29 *************** *** 761,767 **** Var *var = (Var *) node; int this_varno = (int) var->varno; int this_varlevelsup = (int) var->varlevelsup; ! if (this_varno == context->target_varno && this_varlevelsup == context->sublevels_up) { --- 761,767 ---- Var *var = (Var *) node; int this_varno = (int) var->varno; int this_varlevelsup = (int) var->varlevelsup; ! if (this_varno == context->target_varno && this_varlevelsup == context->sublevels_up) { Index: src/backend/utils/adt/ruleutils.c =================================================================== RCS file: /home/projects/pgsql/cvsroot/pgsql/src/backend/utils/adt/ruleutils.c,v retrieving revision 1.82 diff -c -r1.82 ruleutils.c *** src/backend/utils/adt/ruleutils.c 2001/08/12 21:35:19 1.82 --- src/backend/utils/adt/ruleutils.c 2001/08/29 16:36:30 *************** *** 989,994 **** --- 989,997 ---- char *sep; List *l; + /* Only one target list allowed */ + Assert(length(query->targetLists) == 1); + /* * If the Query node has a setOperations tree, then it's the top level * of a UNION/INTERSECT/EXCEPT query; only the ORDER BY and LIMIT *************** *** 1017,1023 **** char *opname; appendStringInfo(buf, sep); ! get_rule_sortgroupclause(srt, query->targetList, force_colno, context); opname = get_opname(srt->sortop); if (strcmp(opname, "<") != 0) --- 1020,1026 ---- char *opname; appendStringInfo(buf, sep); ! get_rule_sortgroupclause(srt, lfirst(query->targetLists), force_colno, context); opname = get_opname(srt->sortop); if (strcmp(opname, "<") != 0) *************** *** 1053,1060 **** { StringInfo buf = context->buf; char *sep; ! List *l; /* * Build up the query string - first we say SELECT */ --- 1056,1067 ---- { StringInfo buf = context->buf; char *sep; ! List *l, *tlist; + /* Only one target list */ + Assert(length(query->targetLists) == 1); + tlist = lfirst(query->targetLists); + /* * Build up the query string - first we say SELECT */ *************** *** 1072,1079 **** SortClause *srt = (SortClause *) lfirst(l); appendStringInfo(buf, sep); ! get_rule_sortgroupclause(srt, query->targetList, ! false, context); sep = ", "; } appendStringInfo(buf, ")"); --- 1079,1085 ---- SortClause *srt = (SortClause *) lfirst(l); appendStringInfo(buf, sep); ! get_rule_sortgroupclause(srt, tlist, false, context); sep = ", "; } appendStringInfo(buf, ")"); *************** *** 1084,1090 **** /* Then we tell what to select (the targetlist) */ sep = " "; ! foreach(l, query->targetList) { TargetEntry *tle = (TargetEntry *) lfirst(l); bool tell_as = false; --- 1090,1097 ---- /* Then we tell what to select (the targetlist) */ sep = " "; ! ! foreach(l, tlist) { TargetEntry *tle = (TargetEntry *) lfirst(l); bool tell_as = false; *************** *** 1138,1145 **** GroupClause *grp = (GroupClause *) lfirst(l); appendStringInfo(buf, sep); ! get_rule_sortgroupclause(grp, query->targetList, ! false, context); sep = ", "; } } --- 1145,1151 ---- GroupClause *grp = (GroupClause *) lfirst(l); appendStringInfo(buf, sep); ! get_rule_sortgroupclause(grp, tlist, false, context); sep = ", "; } } *************** *** 1269,1281 **** /* Add the insert-column-names list */ sep = " ("; ! foreach(l, query->targetList) { TargetEntry *tle = (TargetEntry *) lfirst(l); ! if (tle->resdom->resjunk) continue; /* ignore junk entries */ ! appendStringInfo(buf, sep); sep = ", "; appendStringInfo(buf, "%s", quote_identifier(tle->resdom->resname)); --- 1275,1287 ---- /* Add the insert-column-names list */ sep = " ("; ! foreach(l, lfirst(query->targetLists)) { TargetEntry *tle = (TargetEntry *) lfirst(l); ! if (tle->resdom->resjunk) continue; /* ignore junk entries */ ! appendStringInfo(buf, sep); sep = ", "; appendStringInfo(buf, "%s", quote_identifier(tle->resdom->resname)); *************** *** 1285,1304 **** /* Add the VALUES or the SELECT */ if (select_rte == NULL) { ! appendStringInfo(buf, "VALUES ("); sep = ""; ! foreach(l, query->targetList) ! { ! TargetEntry *tle = (TargetEntry *) lfirst(l); ! if (tle->resdom->resjunk) ! continue; /* ignore junk entries */ ! appendStringInfo(buf, sep); ! sep = ", "; ! get_tle_expr(tle, context); } - appendStringInfoChar(buf, ')'); } else get_query_def(select_rte->subquery, buf, NIL); --- 1291,1319 ---- /* Add the VALUES or the SELECT */ if (select_rte == NULL) { ! int num = length(query->targetLists), i = 1; ! ! appendStringInfo(buf, "VALUES"); sep = ""; ! foreach(l, query->targetLists) { ! List *tl = lfirst(l), *telem; ! appendStringInfo(buf, " ("); ! foreach(telem, tl) ! { ! TargetEntry *tle = (TargetEntry *) lfirst(telem); ! if (tle->resdom->resjunk) ! continue; /* ignore junk entries */ ! ! appendStringInfo(buf, sep); ! sep = ", "; ! get_tle_expr(tle, context); ! } ! appendStringInfoChar(buf, ')'); ! if (i++ != num) ! appendStringInfoChar(buf, ','); } } else get_query_def(select_rte->subquery, buf, NIL); *************** *** 1315,1322 **** StringInfo buf = context->buf; char *sep; RangeTblEntry *rte; ! List *l; /* * Start the query with UPDATE relname SET */ --- 1330,1341 ---- StringInfo buf = context->buf; char *sep; RangeTblEntry *rte; ! List *l, *tlist; + /* only one targetlist allowed */ + Assert(length(query->targetLists) == 1); + tlist = lfirst(query->targetLists); + /* * Start the query with UPDATE relname SET */ *************** *** 1327,1333 **** /* Add the comma separated list of 'attname = value' */ sep = ""; ! foreach(l, query->targetList) { TargetEntry *tle = (TargetEntry *) lfirst(l); --- 1346,1352 ---- /* Add the comma separated list of 'attname = value' */ sep = ""; ! foreach(l, tlist) { TargetEntry *tle = (TargetEntry *) lfirst(l); Index: src/include/nodes/execnodes.h =================================================================== RCS file: /home/projects/pgsql/cvsroot/pgsql/src/include/nodes/execnodes.h,v retrieving revision 1.62 diff -c -r1.62 execnodes.h *** src/include/nodes/execnodes.h 2001/07/16 05:07:00 1.62 --- src/include/nodes/execnodes.h 2001/08/29 16:36:30 *************** *** 354,359 **** --- 354,360 ---- CommonState cstate; /* its first field is NodeTag */ bool rs_done; bool rs_checkqual; + List *rs_targetlists; } ResultState; /* ---------------- Index: src/include/nodes/parsenodes.h =================================================================== RCS file: /home/projects/pgsql/cvsroot/pgsql/src/include/nodes/parsenodes.h,v retrieving revision 1.143 diff -c -r1.143 parsenodes.h *** src/include/nodes/parsenodes.h 2001/08/26 16:56:02 1.143 --- src/include/nodes/parsenodes.h 2001/08/29 16:36:30 *************** *** 55,61 **** List *rowMarks; /* integer list of RT indexes of relations * that are selected FOR UPDATE */ ! List *targetList; /* target list (of TargetEntry) */ List *groupClause; /* a list of GroupClause's */ --- 55,61 ---- List *rowMarks; /* integer list of RT indexes of relations * that are selected FOR UPDATE */ ! List *targetLists; /* list of target lists (of TargetEntry's) */ List *groupClause; /* a list of GroupClause's */ *************** *** 806,816 **** 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 */ } InsertStmt; --- 806,817 ---- List *cols; /* optional: names of the target columns */ /* ! * An INSERT statement has *either* VALUES or SELECT, never ! * both. If VALUES, a list of lists of ResTarget's is supplied ! * (empty for DEFAULT VALUES). If SELECT, a complete SelectStmt ! * (or set-operation tree) is supplied. */ ! List *values; /* the values to insert */ Node *selectStmt; /* the source SELECT */ } InsertStmt; Index: src/include/nodes/plannodes.h =================================================================== RCS file: /home/projects/pgsql/cvsroot/pgsql/src/include/nodes/plannodes.h,v retrieving revision 1.49 diff -c -r1.49 plannodes.h *** src/include/nodes/plannodes.h 2001/03/22 04:00:52 1.49 --- src/include/nodes/plannodes.h 2001/08/29 16:36:30 *************** *** 143,148 **** --- 143,149 ---- typedef struct Result { Plan plan; + List *targetlists; Node *resconstantqual; ResultState *resstate; } Result; Index: src/include/optimizer/planmain.h =================================================================== RCS file: /home/projects/pgsql/cvsroot/pgsql/src/include/optimizer/planmain.h,v retrieving revision 1.51 diff -c -r1.51 planmain.h *** src/include/optimizer/planmain.h 2001/06/05 05:26:05 1.51 --- src/include/optimizer/planmain.h 2001/08/29 16:36:30 *************** *** 42,48 **** Node *limitOffset, Node *limitCount); extern SetOp *make_setop(SetOpCmd cmd, List *tlist, Plan *lefttree, List *distinctList, AttrNumber flagColIdx); ! extern Result *make_result(List *tlist, Node *resconstantqual, Plan *subplan); /* * prototypes for plan/initsplan.c --- 42,48 ---- Node *limitOffset, Node *limitCount); extern SetOp *make_setop(SetOpCmd cmd, List *tlist, Plan *lefttree, List *distinctList, AttrNumber flagColIdx); ! extern Result *make_result(List *tlists, Node *resconstantqual, Plan *subplan); /* * prototypes for plan/initsplan.c Index: src/interfaces/ecpg/preproc/preproc.y =================================================================== RCS file: /home/projects/pgsql/cvsroot/pgsql/src/interfaces/ecpg/preproc/preproc.y,v retrieving revision 1.152 diff -c -r1.152 preproc.y *** src/interfaces/ecpg/preproc/preproc.y 2001/08/19 09:21:44 1.152 --- src/interfaces/ecpg/preproc/preproc.y 2001/08/29 16:36:31 *************** *** 340,345 **** --- 340,346 ---- %type opt_force key_update CreateSchemaStmt PosIntStringConst %type IntConst PosIntConst grantee_list func_type %type select_limit opt_for_update_clause CheckPointStmt + %type values_list %type ECPGWhenever ECPGConnect connection_target ECPGOpen %type indicator ECPGExecute ECPGPrepare ecpg_using *************** *** 2371,2379 **** } ; ! insert_rest: VALUES '(' target_list ')' { ! $$ = cat_str(3, make_str("values("), $3, make_str(")")); } | DEFAULT VALUES { --- 2372,2380 ---- } ; ! insert_rest: VALUES values_list { ! $$ = cat_str(2, make_str("values "), $2); } | DEFAULT VALUES { *************** *** 2383,2397 **** { $$ = $1; } ! | '(' columnList ')' VALUES '(' target_list ')' { ! $$ = cat_str(5, make_str("("), $2, make_str(") values ("), $6, make_str(")")); } | '(' columnList ')' SelectStmt { $$ = cat_str(4, make_str("("), $2, make_str(")"), $4); } ; opt_column_list: '(' columnList ')' { $$ = cat_str(3, make_str("("), $2, make_str(")")); } | /*EMPTY*/ { $$ = EMPTY; } --- 2384,2404 ---- { $$ = $1; } ! | '(' columnList ')' VALUES values_list { ! $$ = cat_str(4, make_str("("), $2, make_str(") values "), $5); } | '(' columnList ')' SelectStmt { $$ = cat_str(4, make_str("("), $2, make_str(")"), $4); } ; + + values_list: values_list ',' '(' target_list ')' + { $$ = cat_str(4, $1, make_str(", ("), $4, make_str(")")); } + | '(' target_list ')' + { $$ = cat_str(3, make_str("("), $2, make_str(")")); } + ; opt_column_list: '(' columnList ')' { $$ = cat_str(3, make_str("("), $2, make_str(")")); } | /*EMPTY*/ { $$ = EMPTY; } Index: src/test/regress/parallel_schedule =================================================================== RCS file: /home/projects/pgsql/cvsroot/pgsql/src/test/regress/parallel_schedule,v retrieving revision 1.5 diff -c -r1.5 parallel_schedule *** src/test/regress/parallel_schedule 2001/06/12 16:34:27 1.5 --- src/test/regress/parallel_schedule 2001/08/29 16:36:31 *************** *** 37,43 **** # ---------- # The third group of parallel test # ---------- ! test: constraints triggers create_misc create_aggregate create_operator create_index inherit # Depends on the above test: create_view --- 37,43 ---- # ---------- # The third group of parallel test # ---------- ! test: constraints triggers create_misc create_aggregate create_operator create_index inherit insert # Depends on the above test: create_view Index: src/test/regress/serial_schedule =================================================================== RCS file: /home/projects/pgsql/cvsroot/pgsql/src/test/regress/serial_schedule,v retrieving revision 1.6 diff -c -r1.6 serial_schedule *** src/test/regress/serial_schedule 2001/06/12 16:34:27 1.6 --- src/test/regress/serial_schedule 2001/08/29 16:36:31 *************** *** 47,52 **** --- 47,53 ---- test: create_operator test: create_index test: inherit + test: insert test: create_view test: sanity_check test: errors