diff options
author | Joe Conway <mail@joeconway.com> | 2006-08-02 01:59:48 +0000 |
---|---|---|
committer | Joe Conway <mail@joeconway.com> | 2006-08-02 01:59:48 +0000 |
commit | 9caafda579f699b43fa4c89bf13a2331ef00611e (patch) | |
tree | 330423c4be56ffaacb2d028153706f0c213c0aec /src/backend/optimizer | |
parent | d307c428cbb7c426e40163d234d993e644bbcc6b (diff) | |
download | postgresql-9caafda579f699b43fa4c89bf13a2331ef00611e.tar.gz postgresql-9caafda579f699b43fa4c89bf13a2331ef00611e.zip |
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.
Diffstat (limited to 'src/backend/optimizer')
-rw-r--r-- | src/backend/optimizer/path/allpaths.c | 26 | ||||
-rw-r--r-- | src/backend/optimizer/path/costsize.c | 63 | ||||
-rw-r--r-- | src/backend/optimizer/plan/createplan.c | 69 | ||||
-rw-r--r-- | src/backend/optimizer/plan/planner.c | 31 | ||||
-rw-r--r-- | src/backend/optimizer/plan/setrefs.c | 20 | ||||
-rw-r--r-- | src/backend/optimizer/plan/subselect.c | 13 | ||||
-rw-r--r-- | src/backend/optimizer/util/clauses.c | 9 | ||||
-rw-r--r-- | src/backend/optimizer/util/pathnode.c | 21 | ||||
-rw-r--r-- | src/backend/optimizer/util/plancat.c | 23 | ||||
-rw-r--r-- | src/backend/optimizer/util/relnode.c | 11 |
10 files changed, 259 insertions, 27 deletions
diff --git a/src/backend/optimizer/path/allpaths.c b/src/backend/optimizer/path/allpaths.c index 05fe268a318..1d7e20c1e8b 100644 --- a/src/backend/optimizer/path/allpaths.c +++ b/src/backend/optimizer/path/allpaths.c @@ -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. * diff --git a/src/backend/optimizer/path/costsize.c b/src/backend/optimizer/path/costsize.c index 5f5e9ec3721..fffa25dd844 100644 --- a/src/backend/optimizer/path/costsize.c +++ b/src/backend/optimizer/path/costsize.c @@ -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 diff --git a/src/backend/optimizer/plan/createplan.c b/src/backend/optimizer/plan/createplan.c index 7ed718f0b31..ae51505954f 100644 --- a/src/backend/optimizer/plan/createplan.c +++ b/src/backend/optimizer/plan/createplan.c @@ -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) { diff --git a/src/backend/optimizer/plan/planner.c b/src/backend/optimizer/plan/planner.c index c02f6e195cd..42ae15cd483 100644 --- a/src/backend/optimizer/plan/planner.c +++ b/src/backend/optimizer/plan/planner.c @@ -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); diff --git a/src/backend/optimizer/plan/setrefs.c b/src/backend/optimizer/plan/setrefs.c index 2529c42ad95..11ef4765d86 100644 --- a/src/backend/optimizer/plan/setrefs.c +++ b/src/backend/optimizer/plan/setrefs.c @@ -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); diff --git a/src/backend/optimizer/plan/subselect.c b/src/backend/optimizer/plan/subselect.c index 0bc9a46c952..95e560478d9 100644 --- a/src/backend/optimizer/plan/subselect.c +++ b/src/backend/optimizer/plan/subselect.c @@ -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; diff --git a/src/backend/optimizer/util/clauses.c b/src/backend/optimizer/util/clauses.c index dfc43149d3a..5570b33f485 100644 --- a/src/backend/optimizer/util/clauses.c +++ b/src/backend/optimizer/util/clauses.c @@ -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); } diff --git a/src/backend/optimizer/util/pathnode.c b/src/backend/optimizer/util/pathnode.c index 631d6087d8e..2cc79ed239e 100644 --- a/src/backend/optimizer/util/pathnode.c +++ b/src/backend/optimizer/util/pathnode.c @@ -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. diff --git a/src/backend/optimizer/util/plancat.c b/src/backend/optimizer/util/plancat.c index bafe1b66731..f74faa5c977 100644 --- a/src/backend/optimizer/util/plancat.c +++ b/src/backend/optimizer/util/plancat.c @@ -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", diff --git a/src/backend/optimizer/util/relnode.c b/src/backend/optimizer/util/relnode.c index 8d06254a9f4..545b125197c 100644 --- a/src/backend/optimizer/util/relnode.c +++ b/src/backend/optimizer/util/relnode.c @@ -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 *) |