1
0
mirror of https://github.com/postgres/postgres.git synced 2025-08-27 07:42:10 +03:00

Add support for multi-row VALUES clauses as part of INSERT statements

(e.g. "INSERT ... VALUES (...), (...), ...") and elsewhere as allowed
by the spec. (e.g. similar to a FROM clause subselect). initdb required.
Joe Conway and Tom Lane.
This commit is contained in:
Joe Conway
2006-08-02 01:59:48 +00:00
parent d307c428cb
commit 9caafda579
40 changed files with 1877 additions and 313 deletions

View 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 */
@@ -537,6 +544,23 @@ set_function_pathlist(PlannerInfo *root, RelOptInfo *rel, RangeTblEntry *rte)
set_cheapest(rel);
}
/*
* set_values_pathlist
* Build the (single) access path for a VALUES RTE
*/
static void
set_values_pathlist(PlannerInfo *root, RelOptInfo *rel, RangeTblEntry *rte)
{
/* Mark rel with estimated output rows, width, etc */
set_values_size_estimates(root, rel);
/* Generate appropriate path */
add_path(rel, create_valuesscan_path(root, rel));
/* Select cheapest path (pretty easy in this case...) */
set_cheapest(rel);
}
/*
* make_rel_from_joinlist
* Build access paths using a "joinlist" to guide the join path search.

View 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 $
*
*-------------------------------------------------------------------------
*/
@@ -774,6 +774,36 @@ cost_functionscan(Path *path, PlannerInfo *root, RelOptInfo *baserel)
path->total_cost = startup_cost + run_cost;
}
/*
* cost_valuesscan
* Determines and returns the cost of scanning a VALUES RTE.
*/
void
cost_valuesscan(Path *path, PlannerInfo *root, RelOptInfo *baserel)
{
Cost startup_cost = 0;
Cost run_cost = 0;
Cost cpu_per_tuple;
/* Should only be applied to base relations that are values lists */
Assert(baserel->relid > 0);
Assert(baserel->rtekind == RTE_VALUES);
/*
* For now, estimate list evaluation cost at one operator eval per
* list (probably pretty bogus, but is it worth being smarter?)
*/
cpu_per_tuple = cpu_operator_cost;
/* Add scanning CPU costs */
startup_cost += baserel->baserestrictcost.startup;
cpu_per_tuple += cpu_tuple_cost + baserel->baserestrictcost.per_tuple;
run_cost += cpu_per_tuple * baserel->tuples;
path->startup_cost = startup_cost;
path->total_cost = startup_cost + run_cost;
}
/*
* cost_sort
* Determines and returns the cost of sorting a relation, including
@@ -2023,6 +2053,37 @@ set_function_size_estimates(PlannerInfo *root, RelOptInfo *rel)
set_baserel_size_estimates(root, rel);
}
/*
* set_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

View 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)
{

View 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);

View 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);

View 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;

View 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);
}

View 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 $
*
*-------------------------------------------------------------------------
*/
@@ -1082,6 +1082,25 @@ create_functionscan_path(PlannerInfo *root, RelOptInfo *rel)
return pathnode;
}
/*
* create_valuesscan_path
* Creates a path corresponding to a scan of a VALUES list,
* returning the pathnode.
*/
Path *
create_valuesscan_path(PlannerInfo *root, RelOptInfo *rel)
{
Path *pathnode = makeNode(Path);
pathnode->pathtype = T_ValuesScan;
pathnode->parent = rel;
pathnode->pathkeys = NIL; /* result is always unordered */
cost_valuesscan(pathnode, root, rel);
return pathnode;
}
/*
* create_nestloop_path
* Creates a pathnode corresponding to a nestloop join between two

View 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",

View 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 *)