|
|
|
/*-------------------------------------------------------------------------
|
|
|
|
*
|
|
|
|
* setrefs.c
|
|
|
|
* Post-processing of a completed plan tree: fix references to subplan
|
|
|
|
* vars, compute regproc values for operators, etc
|
|
|
|
*
|
|
|
|
* Portions Copyright (c) 1996-2018, PostgreSQL Global Development Group
|
|
|
|
* Portions Copyright (c) 1994, Regents of the University of California
|
|
|
|
*
|
|
|
|
*
|
|
|
|
* IDENTIFICATION
|
|
|
|
* src/backend/optimizer/plan/setrefs.c
|
|
|
|
*
|
|
|
|
*-------------------------------------------------------------------------
|
|
|
|
*/
|
|
|
|
#include "postgres.h"
|
|
|
|
|
|
|
|
#include "access/transam.h"
|
|
|
|
#include "catalog/pg_type.h"
|
|
|
|
#include "nodes/makefuncs.h"
|
|
|
|
#include "nodes/nodeFuncs.h"
|
|
|
|
#include "optimizer/pathnode.h"
|
|
|
|
#include "optimizer/planmain.h"
|
|
|
|
#include "optimizer/planner.h"
|
|
|
|
#include "optimizer/tlist.h"
|
Restructure SELECT INTO's parsetree representation into CreateTableAsStmt.
Making this operation look like a utility statement seems generally a good
idea, and particularly so in light of the desire to provide command
triggers for utility statements. The original choice of representing it as
SELECT with an IntoClause appendage had metastasized into rather a lot of
places, unfortunately, so that this patch is a great deal more complicated
than one might at first expect.
In particular, keeping EXPLAIN working for SELECT INTO and CREATE TABLE AS
subcommands required restructuring some EXPLAIN-related APIs. Add-on code
that calls ExplainOnePlan or ExplainOneUtility, or uses
ExplainOneQuery_hook, will need adjustment.
Also, the cases PREPARE ... SELECT INTO and CREATE RULE ... SELECT INTO,
which formerly were accepted though undocumented, are no longer accepted.
The PREPARE case can be replaced with use of CREATE TABLE AS EXECUTE.
The CREATE RULE case doesn't seem to have much real-world use (since the
rule would work only once before failing with "table already exists"),
so we'll not bother with that one.
Both SELECT INTO and CREATE TABLE AS still return a command tag of
"SELECT nnnn". There was some discussion of returning "CREATE TABLE nnnn",
but for the moment backwards compatibility wins the day.
Andres Freund and Tom Lane
14 years ago
|
|
|
#include "tcop/utility.h"
|
|
|
|
#include "utils/lsyscache.h"
|
|
|
|
#include "utils/syscache.h"
|
|
|
|
|
|
|
|
|
|
|
|
typedef struct
|
|
|
|
{
|
|
|
|
Index varno; /* RT index of Var */
|
|
|
|
AttrNumber varattno; /* attr number of Var */
|
|
|
|
AttrNumber resno; /* TLE position of Var */
|
|
|
|
} tlist_vinfo;
|
|
|
|
|
|
|
|
typedef struct
|
|
|
|
{
|
|
|
|
List *tlist; /* underlying target list */
|
|
|
|
int num_vars; /* number of plain Var tlist entries */
|
|
|
|
bool has_ph_vars; /* are there PlaceHolderVar entries? */
|
|
|
|
bool has_non_vars; /* are there other entries? */
|
|
|
|
tlist_vinfo vars[FLEXIBLE_ARRAY_MEMBER]; /* has num_vars entries */
|
|
|
|
} indexed_tlist;
|
|
|
|
|
|
|
|
typedef struct
|
|
|
|
{
|
|
|
|
PlannerInfo *root;
|
|
|
|
int rtoffset;
|
|
|
|
} fix_scan_expr_context;
|
|
|
|
|
|
|
|
typedef struct
|
|
|
|
{
|
|
|
|
PlannerInfo *root;
|
|
|
|
indexed_tlist *outer_itlist;
|
|
|
|
indexed_tlist *inner_itlist;
|
|
|
|
Index acceptable_rel;
|
|
|
|
int rtoffset;
|
|
|
|
} fix_join_expr_context;
|
|
|
|
|
|
|
|
typedef struct
|
|
|
|
{
|
|
|
|
PlannerInfo *root;
|
|
|
|
indexed_tlist *subplan_itlist;
|
|
|
|
Index newvarno;
|
|
|
|
int rtoffset;
|
|
|
|
} fix_upper_expr_context;
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Check if a Const node is a regclass value. We accept plain OID too,
|
|
|
|
* since a regclass Const will get folded to that type if it's an argument
|
|
|
|
* to oideq or similar operators. (This might result in some extraneous
|
|
|
|
* values in a plan's list of relation dependencies, but the worst result
|
|
|
|
* would be occasional useless replans.)
|
|
|
|
*/
|
|
|
|
#define ISREGCLASSCONST(con) \
|
|
|
|
(((con)->consttype == REGCLASSOID || (con)->consttype == OIDOID) && \
|
|
|
|
!(con)->constisnull)
|
|
|
|
|
|
|
|
#define fix_scan_list(root, lst, rtoffset) \
|
|
|
|
((List *) fix_scan_expr(root, (Node *) (lst), rtoffset))
|
|
|
|
|
|
|
|
static void add_rtes_to_flat_rtable(PlannerInfo *root, bool recursing);
|
|
|
|
static void flatten_unplanned_rtes(PlannerGlobal *glob, RangeTblEntry *rte);
|
|
|
|
static bool flatten_rtes_walker(Node *node, PlannerGlobal *glob);
|
|
|
|
static void add_rte_to_flat_rtable(PlannerGlobal *glob, RangeTblEntry *rte);
|
|
|
|
static Plan *set_plan_refs(PlannerInfo *root, Plan *plan, int rtoffset);
|
|
|
|
static Plan *set_indexonlyscan_references(PlannerInfo *root,
|
|
|
|
IndexOnlyScan *plan,
|
|
|
|
int rtoffset);
|
|
|
|
static Plan *set_subqueryscan_references(PlannerInfo *root,
|
|
|
|
SubqueryScan *plan,
|
|
|
|
int rtoffset);
|
|
|
|
static bool trivial_subqueryscan(SubqueryScan *plan);
|
Code review for foreign/custom join pushdown patch.
Commit e7cb7ee14555cc9c5773e2c102efd6371f6f2005 included some design
decisions that seem pretty questionable to me, and there was quite a lot
of stuff not to like about the documentation and comments. Clean up
as follows:
* Consider foreign joins only between foreign tables on the same server,
rather than between any two foreign tables with the same underlying FDW
handler function. In most if not all cases, the FDW would simply have had
to apply the same-server restriction itself (far more expensively, both for
lack of caching and because it would be repeated for each combination of
input sub-joins), or else risk nasty bugs. Anyone who's really intent on
doing something outside this restriction can always use the
set_join_pathlist_hook.
* Rename fdw_ps_tlist/custom_ps_tlist to fdw_scan_tlist/custom_scan_tlist
to better reflect what they're for, and allow these custom scan tlists
to be used even for base relations.
* Change make_foreignscan() API to include passing the fdw_scan_tlist
value, since the FDW is required to set that. Backwards compatibility
doesn't seem like an adequate reason to expect FDWs to set it in some
ad-hoc extra step, and anyway existing FDWs can just pass NIL.
* Change the API of path-generating subroutines of add_paths_to_joinrel,
and in particular that of GetForeignJoinPaths and set_join_pathlist_hook,
so that various less-used parameters are passed in a struct rather than
as separate parameter-list entries. The objective here is to reduce the
probability that future additions to those parameter lists will result in
source-level API breaks for users of these hooks. It's possible that this
is even a small win for the core code, since most CPU architectures can't
pass more than half a dozen parameters efficiently anyway. I kept root,
joinrel, outerrel, innerrel, and jointype as separate parameters to reduce
code churn in joinpath.c --- in particular, putting jointype into the
struct would have been problematic because of the subroutines' habit of
changing their local copies of that variable.
* Avoid ad-hocery in ExecAssignScanProjectionInfo. It was probably all
right for it to know about IndexOnlyScan, but if the list is to grow
we should refactor the knowledge out to the callers.
* Restore nodeForeignscan.c's previous use of the relcache to avoid
extra GetFdwRoutine lookups for base-relation scans.
* Lots of cleanup of documentation and missed comments. Re-order some
code additions into more logical places.
10 years ago
|
|
|
static void set_foreignscan_references(PlannerInfo *root,
|
|
|
|
ForeignScan *fscan,
|
|
|
|
int rtoffset);
|
|
|
|
static void set_customscan_references(PlannerInfo *root,
|
|
|
|
CustomScan *cscan,
|
|
|
|
int rtoffset);
|
|
|
|
static Node *fix_scan_expr(PlannerInfo *root, Node *node, int rtoffset);
|
|
|
|
static Node *fix_scan_expr_mutator(Node *node, fix_scan_expr_context *context);
|
|
|
|
static bool fix_scan_expr_walker(Node *node, fix_scan_expr_context *context);
|
|
|
|
static void set_join_references(PlannerInfo *root, Join *join, int rtoffset);
|
|
|
|
static void set_upper_references(PlannerInfo *root, Plan *plan, int rtoffset);
|
|
|
|
static void set_param_references(PlannerInfo *root, Plan *plan);
|
|
|
|
static Node *convert_combining_aggrefs(Node *node, void *context);
|
|
|
|
static void set_dummy_tlist_references(Plan *plan, int rtoffset);
|
|
|
|
static indexed_tlist *build_tlist_index(List *tlist);
|
|
|
|
static Var *search_indexed_tlist_for_var(Var *var,
|
|
|
|
indexed_tlist *itlist,
|
|
|
|
Index newvarno,
|
|
|
|
int rtoffset);
|
|
|
|
static Var *search_indexed_tlist_for_non_var(Expr *node,
|
|
|
|
indexed_tlist *itlist,
|
|
|
|
Index newvarno);
|
|
|
|
static Var *search_indexed_tlist_for_sortgroupref(Expr *node,
|
|
|
|
Index sortgroupref,
|
|
|
|
indexed_tlist *itlist,
|
|
|
|
Index newvarno);
|
|
|
|
static List *fix_join_expr(PlannerInfo *root,
|
|
|
|
List *clauses,
|
|
|
|
indexed_tlist *outer_itlist,
|
|
|
|
indexed_tlist *inner_itlist,
|
|
|
|
Index acceptable_rel, int rtoffset);
|
|
|
|
static Node *fix_join_expr_mutator(Node *node,
|
|
|
|
fix_join_expr_context *context);
|
|
|
|
static Node *fix_upper_expr(PlannerInfo *root,
|
|
|
|
Node *node,
|
|
|
|
indexed_tlist *subplan_itlist,
|
|
|
|
Index newvarno,
|
|
|
|
int rtoffset);
|
|
|
|
static Node *fix_upper_expr_mutator(Node *node,
|
|
|
|
fix_upper_expr_context *context);
|
Fix planner's handling of RETURNING lists in writable CTEs.
setrefs.c failed to do "rtoffset" adjustment of Vars in RETURNING lists,
which meant they were left with the wrong varnos when the RETURNING list
was in a subquery. That was never possible before writable CTEs, of
course, but now it's broken. The executor fails to notice any problem
because ExecEvalVar just references the ecxt_scantuple for any normal
varno; but EXPLAIN breaks when the varno is wrong, as illustrated in a
recent complaint from Bartosz Dmytrak.
Since the eventual rtoffset of the subquery is not known at the time
we are preparing its plan node, the previous scheme of executing
set_returning_clause_references() at that time cannot handle this
adjustment. Fortunately, it turns out that we don't really need to do it
that way, because all the needed information is available during normal
setrefs.c execution; we just have to dig it out of the ModifyTable node.
So, do that, and get rid of the kluge of early setrefs processing of
RETURNING lists. (This is a little bit of a cheat in the case of inherited
UPDATE/DELETE, because we are not passing a "root" struct that corresponds
exactly to what the subplan was built with. But that doesn't matter, and
anyway this is less ugly than early setrefs processing was.)
Back-patch to 9.1, where the problem became possible to hit.
14 years ago
|
|
|
static List *set_returning_clause_references(PlannerInfo *root,
|
|
|
|
List *rlist,
|
|
|
|
Plan *topplan,
|
|
|
|
Index resultRelation,
|
|
|
|
int rtoffset);
|
|
|
|
static bool extract_query_dependencies_walker(Node *node,
|
|
|
|
PlannerInfo *context);
|
|
|
|
|
|
|
|
/*****************************************************************************
|
|
|
|
*
|
|
|
|
* SUBPLAN REFERENCES
|
|
|
|
*
|
|
|
|
*****************************************************************************/
|
|
|
|
|
|
|
|
/*
|
|
|
|
* set_plan_references
|
|
|
|
*
|
|
|
|
* This is the final processing pass of the planner/optimizer. The plan
|
|
|
|
* tree is complete; we just have to adjust some representational details
|
|
|
|
* for the convenience of the executor:
|
|
|
|
*
|
|
|
|
* 1. We flatten the various subquery rangetables into a single list, and
|
|
|
|
* zero out RangeTblEntry fields that are not useful to the executor.
|
|
|
|
*
|
|
|
|
* 2. We adjust Vars in scan nodes to be consistent with the flat rangetable.
|
|
|
|
*
|
|
|
|
* 3. We adjust Vars in upper plan nodes to refer to the outputs of their
|
|
|
|
* subplans.
|
|
|
|
*
|
|
|
|
* 4. Aggrefs in Agg plan nodes need to be adjusted in some cases involving
|
|
|
|
* partial aggregation or minmax aggregate optimization.
|
|
|
|
*
|
|
|
|
* 5. PARAM_MULTIEXPR Params are replaced by regular PARAM_EXEC Params,
|
Implement UPDATE tab SET (col1,col2,...) = (SELECT ...), ...
This SQL-standard feature allows a sub-SELECT yielding multiple columns
(but only one row) to be used to compute the new values of several columns
to be updated. While the same results can be had with an independent
sub-SELECT per column, such a workaround can require a great deal of
duplicated computation.
The standard actually says that the source for a multi-column assignment
could be any row-valued expression. The implementation used here is
tightly tied to our existing sub-SELECT support and can't handle other
cases; the Bison grammar would have some issues with them too. However,
I don't feel too bad about this since other cases can be converted into
sub-SELECTs. For instance, "SET (a,b,c) = row_valued_function(x)" could
be written "SET (a,b,c) = (SELECT * FROM row_valued_function(x))".
11 years ago
|
|
|
* now that we have finished planning all MULTIEXPR subplans.
|
|
|
|
*
|
|
|
|
* 6. We compute regproc OIDs for operators (ie, we look up the function
|
|
|
|
* that implements each op).
|
|
|
|
*
|
|
|
|
* 7. We create lists of specific objects that the plan depends on.
|
|
|
|
* This will be used by plancache.c to drive invalidation of cached plans.
|
|
|
|
* Relation dependencies are represented by OIDs, and everything else by
|
|
|
|
* PlanInvalItems (this distinction is motivated by the shared-inval APIs).
|
|
|
|
* Currently, relations and user-defined functions are the only types of
|
|
|
|
* objects that are explicitly tracked this way.
|
|
|
|
*
|
|
|
|
* 8. We assign every plan node in the tree a unique ID.
|
|
|
|
*
|
|
|
|
* We also perform one final optimization step, which is to delete
|
|
|
|
* SubqueryScan plan nodes that aren't doing anything useful (ie, have
|
|
|
|
* no qual and a no-op targetlist). The reason for doing this last is that
|
|
|
|
* it can't readily be done before set_plan_references, because it would
|
|
|
|
* break set_upper_references: the Vars in the subquery's top tlist
|
|
|
|
* wouldn't match up with the Vars in the outer plan tree. The SubqueryScan
|
|
|
|
* serves a necessary function as a buffer between outer query and subquery
|
|
|
|
* variable numbering ... but after we've flattened the rangetable this is
|
|
|
|
* no longer a problem, since then there's only one rtindex namespace.
|
|
|
|
*
|
|
|
|
* set_plan_references recursively traverses the whole plan tree.
|
|
|
|
*
|
|
|
|
* The return value is normally the same Plan node passed in, but can be
|
|
|
|
* different when the passed-in Plan is a SubqueryScan we decide isn't needed.
|
|
|
|
*
|
|
|
|
* The flattened rangetable entries are appended to root->glob->finalrtable.
|
|
|
|
* Also, rowmarks entries are appended to root->glob->finalrowmarks, and the
|
|
|
|
* RT indexes of ModifyTable result relations to root->glob->resultRelations.
|
|
|
|
* Plan dependencies are appended to root->glob->relationOids (for relations)
|
|
|
|
* and root->glob->invalItems (for everything else).
|
|
|
|
*
|
|
|
|
* Notice that we modify Plan nodes in-place, but use expression_tree_mutator
|
|
|
|
* to process targetlist and qual expressions. We can assume that the Plan
|
|
|
|
* nodes were just built by the planner and are not multiply referenced, but
|
|
|
|
* it's not so safe to assume that for expression tree nodes.
|
|
|
|
*/
|
|
|
|
Plan *
|
|
|
|
set_plan_references(PlannerInfo *root, Plan *plan)
|
|
|
|
{
|
|
|
|
PlannerGlobal *glob = root->glob;
|
|
|
|
int rtoffset = list_length(glob->finalrtable);
|
|
|
|
ListCell *lc;
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Add all the query's RTEs to the flattened rangetable. The live ones
|
|
|
|
* will have their rangetable indexes increased by rtoffset. (Additional
|
|
|
|
* RTEs, not referenced by the Plan tree, might get added after those.)
|
|
|
|
*/
|
|
|
|
add_rtes_to_flat_rtable(root, false);
|
|
|
|
|
|
|
|
/*
|
Re-implement EvalPlanQual processing to improve its performance and eliminate
a lot of strange behaviors that occurred in join cases. We now identify the
"current" row for every joined relation in UPDATE, DELETE, and SELECT FOR
UPDATE/SHARE queries. If an EvalPlanQual recheck is necessary, we jam the
appropriate row into each scan node in the rechecking plan, forcing it to emit
only that one row. The former behavior could rescan the whole of each joined
relation for each recheck, which was terrible for performance, and what's much
worse could result in duplicated output tuples.
Also, the original implementation of EvalPlanQual could not re-use the recheck
execution tree --- it had to go through a full executor init and shutdown for
every row to be tested. To avoid this overhead, I've associated a special
runtime Param with each LockRows or ModifyTable plan node, and arranged to
make every scan node below such a node depend on that Param. Thus, by
signaling a change in that Param, the EPQ machinery can just rescan the
already-built test plan.
This patch also adds a prohibition on set-returning functions in the
targetlist of SELECT FOR UPDATE/SHARE. This is needed to avoid the
duplicate-output-tuple problem. It seems fairly reasonable since the
other restrictions on SELECT FOR UPDATE are meant to ensure that there
is a unique correspondence between source tuples and result tuples,
which an output SRF destroys as much as anything else does.
16 years ago
|
|
|
* Adjust RT indexes of PlanRowMarks and add to final rowmarks list
|
|
|
|
*/
|
|
|
|
foreach(lc, root->rowMarks)
|
|
|
|
{
|
Improve castNode notation by introducing list-extraction-specific variants.
This extends the castNode() notation introduced by commit 5bcab1114 to
provide, in one step, extraction of a list cell's pointer and coercion to
a concrete node type. For example, "lfirst_node(Foo, lc)" is the same
as "castNode(Foo, lfirst(lc))". Almost half of the uses of castNode
that have appeared so far include a list extraction call, so this is
pretty widely useful, and it saves a few more keystrokes compared to the
old way.
As with the previous patch, back-patch the addition of these macros to
pg_list.h, so that the notation will be available when back-patching.
Patch by me, after an idea of Andrew Gierth's.
Discussion: https://postgr.es/m/14197.1491841216@sss.pgh.pa.us
9 years ago
|
|
|
PlanRowMark *rc = lfirst_node(PlanRowMark, lc);
|
Re-implement EvalPlanQual processing to improve its performance and eliminate
a lot of strange behaviors that occurred in join cases. We now identify the
"current" row for every joined relation in UPDATE, DELETE, and SELECT FOR
UPDATE/SHARE queries. If an EvalPlanQual recheck is necessary, we jam the
appropriate row into each scan node in the rechecking plan, forcing it to emit
only that one row. The former behavior could rescan the whole of each joined
relation for each recheck, which was terrible for performance, and what's much
worse could result in duplicated output tuples.
Also, the original implementation of EvalPlanQual could not re-use the recheck
execution tree --- it had to go through a full executor init and shutdown for
every row to be tested. To avoid this overhead, I've associated a special
runtime Param with each LockRows or ModifyTable plan node, and arranged to
make every scan node below such a node depend on that Param. Thus, by
signaling a change in that Param, the EPQ machinery can just rescan the
already-built test plan.
This patch also adds a prohibition on set-returning functions in the
targetlist of SELECT FOR UPDATE/SHARE. This is needed to avoid the
duplicate-output-tuple problem. It seems fairly reasonable since the
other restrictions on SELECT FOR UPDATE are meant to ensure that there
is a unique correspondence between source tuples and result tuples,
which an output SRF destroys as much as anything else does.
16 years ago
|
|
|
PlanRowMark *newrc;
|
|
|
|
|
Re-implement EvalPlanQual processing to improve its performance and eliminate
a lot of strange behaviors that occurred in join cases. We now identify the
"current" row for every joined relation in UPDATE, DELETE, and SELECT FOR
UPDATE/SHARE queries. If an EvalPlanQual recheck is necessary, we jam the
appropriate row into each scan node in the rechecking plan, forcing it to emit
only that one row. The former behavior could rescan the whole of each joined
relation for each recheck, which was terrible for performance, and what's much
worse could result in duplicated output tuples.
Also, the original implementation of EvalPlanQual could not re-use the recheck
execution tree --- it had to go through a full executor init and shutdown for
every row to be tested. To avoid this overhead, I've associated a special
runtime Param with each LockRows or ModifyTable plan node, and arranged to
make every scan node below such a node depend on that Param. Thus, by
signaling a change in that Param, the EPQ machinery can just rescan the
already-built test plan.
This patch also adds a prohibition on set-returning functions in the
targetlist of SELECT FOR UPDATE/SHARE. This is needed to avoid the
duplicate-output-tuple problem. It seems fairly reasonable since the
other restrictions on SELECT FOR UPDATE are meant to ensure that there
is a unique correspondence between source tuples and result tuples,
which an output SRF destroys as much as anything else does.
16 years ago
|
|
|
/* flat copy is enough since all fields are scalars */
|
|
|
|
newrc = (PlanRowMark *) palloc(sizeof(PlanRowMark));
|
|
|
|
memcpy(newrc, rc, sizeof(PlanRowMark));
|
|
|
|
|
|
|
|
/* adjust indexes ... but *not* the rowmarkId */
|
|
|
|
newrc->rti += rtoffset;
|
|
|
|
newrc->prti += rtoffset;
|
|
|
|
|
|
|
|
glob->finalrowmarks = lappend(glob->finalrowmarks, newrc);
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Now fix the Plan tree */
|
|
|
|
return set_plan_refs(root, plan, rtoffset);
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Extract RangeTblEntries from the plan's rangetable, and add to flat rtable
|
|
|
|
*
|
|
|
|
* This can recurse into subquery plans; "recursing" is true if so.
|
|
|
|
*/
|
|
|
|
static void
|
|
|
|
add_rtes_to_flat_rtable(PlannerInfo *root, bool recursing)
|
|
|
|
{
|
|
|
|
PlannerGlobal *glob = root->glob;
|
|
|
|
Index rti;
|
|
|
|
ListCell *lc;
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Add the query's own RTEs to the flattened rangetable.
|
|
|
|
*
|
|
|
|
* At top level, we must add all RTEs so that their indexes in the
|
|
|
|
* flattened rangetable match up with their original indexes. When
|
|
|
|
* recursing, we only care about extracting relation RTEs.
|
|
|
|
*/
|
|
|
|
foreach(lc, root->parse->rtable)
|
|
|
|
{
|
|
|
|
RangeTblEntry *rte = (RangeTblEntry *) lfirst(lc);
|
|
|
|
|
|
|
|
if (!recursing || rte->rtekind == RTE_RELATION)
|
|
|
|
add_rte_to_flat_rtable(glob, rte);
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* If there are any dead subqueries, they are not referenced in the Plan
|
|
|
|
* tree, so we must add RTEs contained in them to the flattened rtable
|
|
|
|
* separately. (If we failed to do this, the executor would not perform
|
|
|
|
* expected permission checks for tables mentioned in such subqueries.)
|
|
|
|
*
|
|
|
|
* Note: this pass over the rangetable can't be combined with the previous
|
|
|
|
* one, because that would mess up the numbering of the live RTEs in the
|
|
|
|
* flattened rangetable.
|
|
|
|
*/
|
|
|
|
rti = 1;
|
|
|
|
foreach(lc, root->parse->rtable)
|
|
|
|
{
|
|
|
|
RangeTblEntry *rte = (RangeTblEntry *) lfirst(lc);
|
|
|
|
|
|
|
|
/*
|
|
|
|
* We should ignore inheritance-parent RTEs: their contents have been
|
|
|
|
* pulled up into our rangetable already. Also ignore any subquery
|
|
|
|
* RTEs without matching RelOptInfos, as they likewise have been
|
|
|
|
* pulled up.
|
|
|
|
*/
|
|
|
|
if (rte->rtekind == RTE_SUBQUERY && !rte->inh &&
|
|
|
|
rti < root->simple_rel_array_size)
|
|
|
|
{
|
|
|
|
RelOptInfo *rel = root->simple_rel_array[rti];
|
|
|
|
|
|
|
|
if (rel != NULL)
|
|
|
|
{
|
Phase 2 of pgindent updates.
Change pg_bsd_indent to follow upstream rules for placement of comments
to the right of code, and remove pgindent hack that caused comments
following #endif to not obey the general rule.
Commit e3860ffa4dd0dad0dd9eea4be9cc1412373a8c89 wasn't actually using
the published version of pg_bsd_indent, but a hacked-up version that
tried to minimize the amount of movement of comments to the right of
code. The situation of interest is where such a comment has to be
moved to the right of its default placement at column 33 because there's
code there. BSD indent has always moved right in units of tab stops
in such cases --- but in the previous incarnation, indent was working
in 8-space tab stops, while now it knows we use 4-space tabs. So the
net result is that in about half the cases, such comments are placed
one tab stop left of before. This is better all around: it leaves
more room on the line for comment text, and it means that in such
cases the comment uniformly starts at the next 4-space tab stop after
the code, rather than sometimes one and sometimes two tabs after.
Also, ensure that comments following #endif are indented the same
as comments following other preprocessor commands such as #else.
That inconsistency turns out to have been self-inflicted damage
from a poorly-thought-through post-indent "fixup" in pgindent.
This patch is much less interesting than the first round of indent
changes, but also bulkier, so I thought it best to separate the effects.
Discussion: https://postgr.es/m/E1dAmxK-0006EE-1r@gemulon.postgresql.org
Discussion: https://postgr.es/m/30527.1495162840@sss.pgh.pa.us
8 years ago
|
|
|
Assert(rel->relid == rti); /* sanity check on array */
|
|
|
|
|
|
|
|
/*
|
|
|
|
* The subquery might never have been planned at all, if it
|
|
|
|
* was excluded on the basis of self-contradictory constraints
|
|
|
|
* in our query level. In this case apply
|
|
|
|
* flatten_unplanned_rtes.
|
|
|
|
*
|
Make the upper part of the planner work by generating and comparing Paths.
I've been saying we needed to do this for more than five years, and here it
finally is. This patch removes the ever-growing tangle of spaghetti logic
that grouping_planner() used to use to try to identify the best plan for
post-scan/join query steps. Now, there is (nearly) independent
consideration of each execution step, and entirely separate construction of
Paths to represent each of the possible ways to do that step. We choose
the best Path or set of Paths using the same add_path() logic that's been
used inside query_planner() for years.
In addition, this patch removes the old restriction that subquery_planner()
could return only a single Plan. It now returns a RelOptInfo containing a
set of Paths, just as query_planner() does, and the parent query level can
use each of those Paths as the basis of a SubqueryScanPath at its level.
This allows finding some optimizations that we missed before, wherein a
subquery was capable of returning presorted data and thereby avoiding a
sort in the parent level, making the overall cost cheaper even though
delivering sorted output was not the cheapest plan for the subquery in
isolation. (A couple of regression test outputs change in consequence of
that. However, there is very little change in visible planner behavior
overall, because the point of this patch is not to get immediate planning
benefits but to create the infrastructure for future improvements.)
There is a great deal left to do here. This patch unblocks a lot of
planner work that was basically impractical in the old code structure,
such as allowing FDWs to implement remote aggregation, or rewriting
plan_set_operations() to allow consideration of multiple implementation
orders for set operations. (The latter will likely require a full
rewrite of plan_set_operations(); what I've done here is only to fix it
to return Paths not Plans.) I have also left unfinished some localized
refactoring in createplan.c and planner.c, because it was not necessary
to get this patch to a working state.
Thanks to Robert Haas, David Rowley, and Amit Kapila for review.
10 years ago
|
|
|
* If it was planned but the result rel is dummy, we assume
|
|
|
|
* that it has been omitted from our plan tree (see
|
|
|
|
* set_subquery_pathlist), and recurse to pull up its RTEs.
|
|
|
|
*
|
|
|
|
* Otherwise, it should be represented by a SubqueryScan node
|
|
|
|
* somewhere in our plan tree, and we'll pull up its RTEs when
|
|
|
|
* we process that plan node.
|
|
|
|
*
|
|
|
|
* However, if we're recursing, then we should pull up RTEs
|
Make the upper part of the planner work by generating and comparing Paths.
I've been saying we needed to do this for more than five years, and here it
finally is. This patch removes the ever-growing tangle of spaghetti logic
that grouping_planner() used to use to try to identify the best plan for
post-scan/join query steps. Now, there is (nearly) independent
consideration of each execution step, and entirely separate construction of
Paths to represent each of the possible ways to do that step. We choose
the best Path or set of Paths using the same add_path() logic that's been
used inside query_planner() for years.
In addition, this patch removes the old restriction that subquery_planner()
could return only a single Plan. It now returns a RelOptInfo containing a
set of Paths, just as query_planner() does, and the parent query level can
use each of those Paths as the basis of a SubqueryScanPath at its level.
This allows finding some optimizations that we missed before, wherein a
subquery was capable of returning presorted data and thereby avoiding a
sort in the parent level, making the overall cost cheaper even though
delivering sorted output was not the cheapest plan for the subquery in
isolation. (A couple of regression test outputs change in consequence of
that. However, there is very little change in visible planner behavior
overall, because the point of this patch is not to get immediate planning
benefits but to create the infrastructure for future improvements.)
There is a great deal left to do here. This patch unblocks a lot of
planner work that was basically impractical in the old code structure,
such as allowing FDWs to implement remote aggregation, or rewriting
plan_set_operations() to allow consideration of multiple implementation
orders for set operations. (The latter will likely require a full
rewrite of plan_set_operations(); what I've done here is only to fix it
to return Paths not Plans.) I have also left unfinished some localized
refactoring in createplan.c and planner.c, because it was not necessary
to get this patch to a working state.
Thanks to Robert Haas, David Rowley, and Amit Kapila for review.
10 years ago
|
|
|
* whether the subquery is dummy or not, because we've found
|
|
|
|
* that some upper query level is treating this one as dummy,
|
|
|
|
* and so we won't scan this level's plan tree at all.
|
|
|
|
*/
|
Make the upper part of the planner work by generating and comparing Paths.
I've been saying we needed to do this for more than five years, and here it
finally is. This patch removes the ever-growing tangle of spaghetti logic
that grouping_planner() used to use to try to identify the best plan for
post-scan/join query steps. Now, there is (nearly) independent
consideration of each execution step, and entirely separate construction of
Paths to represent each of the possible ways to do that step. We choose
the best Path or set of Paths using the same add_path() logic that's been
used inside query_planner() for years.
In addition, this patch removes the old restriction that subquery_planner()
could return only a single Plan. It now returns a RelOptInfo containing a
set of Paths, just as query_planner() does, and the parent query level can
use each of those Paths as the basis of a SubqueryScanPath at its level.
This allows finding some optimizations that we missed before, wherein a
subquery was capable of returning presorted data and thereby avoiding a
sort in the parent level, making the overall cost cheaper even though
delivering sorted output was not the cheapest plan for the subquery in
isolation. (A couple of regression test outputs change in consequence of
that. However, there is very little change in visible planner behavior
overall, because the point of this patch is not to get immediate planning
benefits but to create the infrastructure for future improvements.)
There is a great deal left to do here. This patch unblocks a lot of
planner work that was basically impractical in the old code structure,
such as allowing FDWs to implement remote aggregation, or rewriting
plan_set_operations() to allow consideration of multiple implementation
orders for set operations. (The latter will likely require a full
rewrite of plan_set_operations(); what I've done here is only to fix it
to return Paths not Plans.) I have also left unfinished some localized
refactoring in createplan.c and planner.c, because it was not necessary
to get this patch to a working state.
Thanks to Robert Haas, David Rowley, and Amit Kapila for review.
10 years ago
|
|
|
if (rel->subroot == NULL)
|
|
|
|
flatten_unplanned_rtes(glob, rte);
|
Make the upper part of the planner work by generating and comparing Paths.
I've been saying we needed to do this for more than five years, and here it
finally is. This patch removes the ever-growing tangle of spaghetti logic
that grouping_planner() used to use to try to identify the best plan for
post-scan/join query steps. Now, there is (nearly) independent
consideration of each execution step, and entirely separate construction of
Paths to represent each of the possible ways to do that step. We choose
the best Path or set of Paths using the same add_path() logic that's been
used inside query_planner() for years.
In addition, this patch removes the old restriction that subquery_planner()
could return only a single Plan. It now returns a RelOptInfo containing a
set of Paths, just as query_planner() does, and the parent query level can
use each of those Paths as the basis of a SubqueryScanPath at its level.
This allows finding some optimizations that we missed before, wherein a
subquery was capable of returning presorted data and thereby avoiding a
sort in the parent level, making the overall cost cheaper even though
delivering sorted output was not the cheapest plan for the subquery in
isolation. (A couple of regression test outputs change in consequence of
that. However, there is very little change in visible planner behavior
overall, because the point of this patch is not to get immediate planning
benefits but to create the infrastructure for future improvements.)
There is a great deal left to do here. This patch unblocks a lot of
planner work that was basically impractical in the old code structure,
such as allowing FDWs to implement remote aggregation, or rewriting
plan_set_operations() to allow consideration of multiple implementation
orders for set operations. (The latter will likely require a full
rewrite of plan_set_operations(); what I've done here is only to fix it
to return Paths not Plans.) I have also left unfinished some localized
refactoring in createplan.c and planner.c, because it was not necessary
to get this patch to a working state.
Thanks to Robert Haas, David Rowley, and Amit Kapila for review.
10 years ago
|
|
|
else if (recursing ||
|
|
|
|
IS_DUMMY_REL(fetch_upper_rel(rel->subroot,
|
|
|
|
UPPERREL_FINAL, NULL)))
|
|
|
|
add_rtes_to_flat_rtable(rel->subroot, true);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
rti++;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Extract RangeTblEntries from a subquery that was never planned at all
|
|
|
|
*/
|
|
|
|
static void
|
|
|
|
flatten_unplanned_rtes(PlannerGlobal *glob, RangeTblEntry *rte)
|
|
|
|
{
|
|
|
|
/* Use query_tree_walker to find all RTEs in the parse tree */
|
|
|
|
(void) query_tree_walker(rte->subquery,
|
|
|
|
flatten_rtes_walker,
|
|
|
|
(void *) glob,
|
|
|
|
QTW_EXAMINE_RTES);
|
|
|
|
}
|
|
|
|
|
|
|
|
static bool
|
|
|
|
flatten_rtes_walker(Node *node, PlannerGlobal *glob)
|
|
|
|
{
|
|
|
|
if (node == NULL)
|
|
|
|
return false;
|
|
|
|
if (IsA(node, RangeTblEntry))
|
|
|
|
{
|
|
|
|
RangeTblEntry *rte = (RangeTblEntry *) node;
|
|
|
|
|
|
|
|
/* As above, we need only save relation RTEs */
|
|
|
|
if (rte->rtekind == RTE_RELATION)
|
|
|
|
add_rte_to_flat_rtable(glob, rte);
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
if (IsA(node, Query))
|
|
|
|
{
|
|
|
|
/* Recurse into subselects */
|
|
|
|
return query_tree_walker((Query *) node,
|
|
|
|
flatten_rtes_walker,
|
|
|
|
(void *) glob,
|
|
|
|
QTW_EXAMINE_RTES);
|
|
|
|
}
|
|
|
|
return expression_tree_walker(node, flatten_rtes_walker,
|
|
|
|
(void *) glob);
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Add (a copy of) the given RTE to the final rangetable
|
|
|
|
*
|
|
|
|
* In the flat rangetable, we zero out substructure pointers that are not
|
|
|
|
* needed by the executor; this reduces the storage space and copying cost
|
Redesign tablesample method API, and do extensive code review.
The original implementation of TABLESAMPLE modeled the tablesample method
API on index access methods, which wasn't a good choice because, without
specialized DDL commands, there's no way to build an extension that can
implement a TSM. (Raw inserts into system catalogs are not an acceptable
thing to do, because we can't undo them during DROP EXTENSION, nor will
pg_upgrade behave sanely.) Instead adopt an API more like procedural
language handlers or foreign data wrappers, wherein the only SQL-level
support object needed is a single handler function identified by having
a special return type. This lets us get rid of the supporting catalog
altogether, so that no custom DDL support is needed for the feature.
Adjust the API so that it can support non-constant tablesample arguments
(the original coding assumed we could evaluate the argument expressions at
ExecInitSampleScan time, which is undesirable even if it weren't outright
unsafe), and discourage sampling methods from looking at invisible tuples.
Make sure that the BERNOULLI and SYSTEM methods are genuinely repeatable
within and across queries, as required by the SQL standard, and deal more
honestly with methods that can't support that requirement.
Make a full code-review pass over the tablesample additions, and fix
assorted bugs, omissions, infelicities, and cosmetic issues (such as
failure to put the added code stanzas in a consistent ordering).
Improve EXPLAIN's output of tablesample plans, too.
Back-patch to 9.5 so that we don't have to support the original API
in production.
10 years ago
|
|
|
* for cached plans. We keep only the ctename, alias and eref Alias fields,
|
|
|
|
* which are needed by EXPLAIN, and the selectedCols, insertedCols and
|
|
|
|
* updatedCols bitmaps, which are needed for executor-startup permissions
|
|
|
|
* checking and for trigger event checking.
|
|
|
|
*/
|
|
|
|
static void
|
|
|
|
add_rte_to_flat_rtable(PlannerGlobal *glob, RangeTblEntry *rte)
|
|
|
|
{
|
|
|
|
RangeTblEntry *newrte;
|
|
|
|
|
|
|
|
/* flat copy to duplicate all the scalar fields */
|
|
|
|
newrte = (RangeTblEntry *) palloc(sizeof(RangeTblEntry));
|
|
|
|
memcpy(newrte, rte, sizeof(RangeTblEntry));
|
|
|
|
|
|
|
|
/* zap unneeded sub-structure */
|
Redesign tablesample method API, and do extensive code review.
The original implementation of TABLESAMPLE modeled the tablesample method
API on index access methods, which wasn't a good choice because, without
specialized DDL commands, there's no way to build an extension that can
implement a TSM. (Raw inserts into system catalogs are not an acceptable
thing to do, because we can't undo them during DROP EXTENSION, nor will
pg_upgrade behave sanely.) Instead adopt an API more like procedural
language handlers or foreign data wrappers, wherein the only SQL-level
support object needed is a single handler function identified by having
a special return type. This lets us get rid of the supporting catalog
altogether, so that no custom DDL support is needed for the feature.
Adjust the API so that it can support non-constant tablesample arguments
(the original coding assumed we could evaluate the argument expressions at
ExecInitSampleScan time, which is undesirable even if it weren't outright
unsafe), and discourage sampling methods from looking at invisible tuples.
Make sure that the BERNOULLI and SYSTEM methods are genuinely repeatable
within and across queries, as required by the SQL standard, and deal more
honestly with methods that can't support that requirement.
Make a full code-review pass over the tablesample additions, and fix
assorted bugs, omissions, infelicities, and cosmetic issues (such as
failure to put the added code stanzas in a consistent ordering).
Improve EXPLAIN's output of tablesample plans, too.
Back-patch to 9.5 so that we don't have to support the original API
in production.
10 years ago
|
|
|
newrte->tablesample = NULL;
|
|
|
|
newrte->subquery = NULL;
|
|
|
|
newrte->joinaliasvars = NIL;
|
Support multi-argument UNNEST(), and TABLE() syntax for multiple functions.
This patch adds the ability to write TABLE( function1(), function2(), ...)
as a single FROM-clause entry. The result is the concatenation of the
first row from each function, followed by the second row from each
function, etc; with NULLs inserted if any function produces fewer rows than
others. This is believed to be a much more useful behavior than what
Postgres currently does with multiple SRFs in a SELECT list.
This syntax also provides a reasonable way to combine use of column
definition lists with WITH ORDINALITY: put the column definition list
inside TABLE(), where it's clear that it doesn't control the ordinality
column as well.
Also implement SQL-compliant multiple-argument UNNEST(), by turning
UNNEST(a,b,c) into TABLE(unnest(a), unnest(b), unnest(c)).
The SQL standard specifies TABLE() with only a single function, not
multiple functions, and it seems to require an implicit UNNEST() which is
not what this patch does. There may be something wrong with that reading
of the spec, though, because if it's right then the spec's TABLE() is just
a pointless alternative spelling of UNNEST(). After further review of
that, we might choose to adopt a different syntax for what this patch does,
but in any case this functionality seems clearly worthwhile.
Andrew Gierth, reviewed by Zoltán Böszörményi and Heikki Linnakangas, and
significantly revised by me
12 years ago
|
|
|
newrte->functions = NIL;
|
|
|
|
newrte->tablefunc = NULL;
|
|
|
|
newrte->values_lists = NIL;
|
Fix reporting of column typmods for multi-row VALUES constructs.
expandRTE() and get_rte_attribute_type() reported the exprType() and
exprTypmod() values of the expressions in the first row of the VALUES as
being the column type/typmod returned by the VALUES RTE. That's fine for
the data type, since we coerce all expressions in a column to have the same
common type. But we don't coerce them to have a common typmod, so it was
possible for rows after the first one to return values that violate the
claimed column typmod. This leads to the incorrect result seen in bug
#14448 from Hassan Mahmood, as well as some other corner-case misbehaviors.
The desired behavior is the same as we use in other type-unification
cases: report the common typmod if there is one, but otherwise return -1
indicating no particular constraint. It's cheap for transformValuesClause
to determine the common typmod while transforming a multi-row VALUES, but
it'd be less cheap for expandRTE() and get_rte_attribute_type() to
re-determine that info every time they're asked --- possibly a lot less
cheap, if the VALUES has many rows. Therefore, the best fix is to record
the common typmods explicitly in a list in the VALUES RTE, as we were
already doing for column collations. This looks quite a bit like what
we're doing for CTE RTEs, so we can save a little bit of space and code by
unifying the representation for those two RTE types. They both now share
coltypes/coltypmods/colcollations fields. (At some point it might seem
desirable to populate those fields for all RTE types; but right now it
looks like constructing them for other RTE types would add more code and
cycles than it would save.)
The RTE change requires a catversion bump, so this fix is only usable
in HEAD. If we fix this at all in the back branches, the patch will
need to look quite different.
Report: https://postgr.es/m/20161205143037.4377.60754@wrigleys.postgresql.org
Discussion: https://postgr.es/m/27429.1480968538@sss.pgh.pa.us
9 years ago
|
|
|
newrte->coltypes = NIL;
|
|
|
|
newrte->coltypmods = NIL;
|
|
|
|
newrte->colcollations = NIL;
|
|
|
|
newrte->securityQuals = NIL;
|
|
|
|
|
|
|
|
glob->finalrtable = lappend(glob->finalrtable, newrte);
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Check for RT index overflow; it's very unlikely, but if it did happen,
|
|
|
|
* the executor would get confused by varnos that match the special varno
|
|
|
|
* values.
|
|
|
|
*/
|
|
|
|
if (IS_SPECIAL_VARNO(list_length(glob->finalrtable)))
|
|
|
|
ereport(ERROR,
|
|
|
|
(errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED),
|
|
|
|
errmsg("too many range table entries")));
|
|
|
|
|
|
|
|
/*
|
|
|
|
* If it's a plain relation RTE, add the table to relationOids.
|
|
|
|
*
|
|
|
|
* We do this even though the RTE might be unreferenced in the plan tree;
|
|
|
|
* this would correspond to cases such as views that were expanded, child
|
|
|
|
* tables that were eliminated by constraint exclusion, etc. Schema
|
|
|
|
* invalidation on such a rel must still force rebuilding of the plan.
|
|
|
|
*
|
|
|
|
* Note we don't bother to avoid making duplicate list entries. We could,
|
|
|
|
* but it would probably cost more cycles than it would save.
|
|
|
|
*/
|
|
|
|
if (newrte->rtekind == RTE_RELATION)
|
|
|
|
glob->relationOids = lappend_oid(glob->relationOids, newrte->relid);
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* set_plan_refs: recurse through the Plan nodes of a single subquery level
|
|
|
|
*/
|
|
|
|
static Plan *
|
|
|
|
set_plan_refs(PlannerInfo *root, Plan *plan, int rtoffset)
|
|
|
|
{
|
|
|
|
ListCell *l;
|
|
|
|
|
|
|
|
if (plan == NULL)
|
|
|
|
return NULL;
|
|
|
|
|
|
|
|
/* Assign this node a unique ID. */
|
|
|
|
plan->plan_node_id = root->glob->lastPlanNodeId++;
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Plan-type-specific fixes
|
|
|
|
*/
|
|
|
|
switch (nodeTag(plan))
|
|
|
|
{
|
|
|
|
case T_SeqScan:
|
|
|
|
{
|
|
|
|
SeqScan *splan = (SeqScan *) plan;
|
|
|
|
|
|
|
|
splan->scanrelid += rtoffset;
|
|
|
|
splan->plan.targetlist =
|
|
|
|
fix_scan_list(root, splan->plan.targetlist, rtoffset);
|
|
|
|
splan->plan.qual =
|
|
|
|
fix_scan_list(root, splan->plan.qual, rtoffset);
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
case T_SampleScan:
|
|
|
|
{
|
|
|
|
SampleScan *splan = (SampleScan *) plan;
|
|
|
|
|
Redesign tablesample method API, and do extensive code review.
The original implementation of TABLESAMPLE modeled the tablesample method
API on index access methods, which wasn't a good choice because, without
specialized DDL commands, there's no way to build an extension that can
implement a TSM. (Raw inserts into system catalogs are not an acceptable
thing to do, because we can't undo them during DROP EXTENSION, nor will
pg_upgrade behave sanely.) Instead adopt an API more like procedural
language handlers or foreign data wrappers, wherein the only SQL-level
support object needed is a single handler function identified by having
a special return type. This lets us get rid of the supporting catalog
altogether, so that no custom DDL support is needed for the feature.
Adjust the API so that it can support non-constant tablesample arguments
(the original coding assumed we could evaluate the argument expressions at
ExecInitSampleScan time, which is undesirable even if it weren't outright
unsafe), and discourage sampling methods from looking at invisible tuples.
Make sure that the BERNOULLI and SYSTEM methods are genuinely repeatable
within and across queries, as required by the SQL standard, and deal more
honestly with methods that can't support that requirement.
Make a full code-review pass over the tablesample additions, and fix
assorted bugs, omissions, infelicities, and cosmetic issues (such as
failure to put the added code stanzas in a consistent ordering).
Improve EXPLAIN's output of tablesample plans, too.
Back-patch to 9.5 so that we don't have to support the original API
in production.
10 years ago
|
|
|
splan->scan.scanrelid += rtoffset;
|
|
|
|
splan->scan.plan.targetlist =
|
|
|
|
fix_scan_list(root, splan->scan.plan.targetlist, rtoffset);
|
|
|
|
splan->scan.plan.qual =
|
|
|
|
fix_scan_list(root, splan->scan.plan.qual, rtoffset);
|
|
|
|
splan->tablesample = (TableSampleClause *)
|
|
|
|
fix_scan_expr(root, (Node *) splan->tablesample, rtoffset);
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
case T_IndexScan:
|
|
|
|
{
|
|
|
|
IndexScan *splan = (IndexScan *) plan;
|
|
|
|
|
|
|
|
splan->scan.scanrelid += rtoffset;
|
|
|
|
splan->scan.plan.targetlist =
|
|
|
|
fix_scan_list(root, splan->scan.plan.targetlist, rtoffset);
|
|
|
|
splan->scan.plan.qual =
|
|
|
|
fix_scan_list(root, splan->scan.plan.qual, rtoffset);
|
|
|
|
splan->indexqual =
|
|
|
|
fix_scan_list(root, splan->indexqual, rtoffset);
|
|
|
|
splan->indexqualorig =
|
|
|
|
fix_scan_list(root, splan->indexqualorig, rtoffset);
|
|
|
|
splan->indexorderby =
|
|
|
|
fix_scan_list(root, splan->indexorderby, rtoffset);
|
|
|
|
splan->indexorderbyorig =
|
|
|
|
fix_scan_list(root, splan->indexorderbyorig, rtoffset);
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
case T_IndexOnlyScan:
|
|
|
|
{
|
|
|
|
IndexOnlyScan *splan = (IndexOnlyScan *) plan;
|
|
|
|
|
|
|
|
return set_indexonlyscan_references(root, splan, rtoffset);
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
case T_BitmapIndexScan:
|
|
|
|
{
|
|
|
|
BitmapIndexScan *splan = (BitmapIndexScan *) plan;
|
|
|
|
|
|
|
|
splan->scan.scanrelid += rtoffset;
|
|
|
|
/* no need to fix targetlist and qual */
|
|
|
|
Assert(splan->scan.plan.targetlist == NIL);
|
|
|
|
Assert(splan->scan.plan.qual == NIL);
|
|
|
|
splan->indexqual =
|
|
|
|
fix_scan_list(root, splan->indexqual, rtoffset);
|
|
|
|
splan->indexqualorig =
|
|
|
|
fix_scan_list(root, splan->indexqualorig, rtoffset);
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
case T_BitmapHeapScan:
|
|
|
|
{
|
|
|
|
BitmapHeapScan *splan = (BitmapHeapScan *) plan;
|
|
|
|
|
|
|
|
splan->scan.scanrelid += rtoffset;
|
|
|
|
splan->scan.plan.targetlist =
|
|
|
|
fix_scan_list(root, splan->scan.plan.targetlist, rtoffset);
|
|
|
|
splan->scan.plan.qual =
|
|
|
|
fix_scan_list(root, splan->scan.plan.qual, rtoffset);
|
|
|
|
splan->bitmapqualorig =
|
|
|
|
fix_scan_list(root, splan->bitmapqualorig, rtoffset);
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
case T_TidScan:
|
|
|
|
{
|
|
|
|
TidScan *splan = (TidScan *) plan;
|
|
|
|
|
|
|
|
splan->scan.scanrelid += rtoffset;
|
|
|
|
splan->scan.plan.targetlist =
|
|
|
|
fix_scan_list(root, splan->scan.plan.targetlist, rtoffset);
|
|
|
|
splan->scan.plan.qual =
|
|
|
|
fix_scan_list(root, splan->scan.plan.qual, rtoffset);
|
|
|
|
splan->tidquals =
|
|
|
|
fix_scan_list(root, splan->tidquals, rtoffset);
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
case T_SubqueryScan:
|
|
|
|
/* Needs special treatment, see comments below */
|
|
|
|
return set_subqueryscan_references(root,
|
|
|
|
(SubqueryScan *) plan,
|
|
|
|
rtoffset);
|
|
|
|
case T_FunctionScan:
|
|
|
|
{
|
|
|
|
FunctionScan *splan = (FunctionScan *) plan;
|
|
|
|
|
|
|
|
splan->scan.scanrelid += rtoffset;
|
|
|
|
splan->scan.plan.targetlist =
|
|
|
|
fix_scan_list(root, splan->scan.plan.targetlist, rtoffset);
|
|
|
|
splan->scan.plan.qual =
|
|
|
|
fix_scan_list(root, splan->scan.plan.qual, rtoffset);
|
Support multi-argument UNNEST(), and TABLE() syntax for multiple functions.
This patch adds the ability to write TABLE( function1(), function2(), ...)
as a single FROM-clause entry. The result is the concatenation of the
first row from each function, followed by the second row from each
function, etc; with NULLs inserted if any function produces fewer rows than
others. This is believed to be a much more useful behavior than what
Postgres currently does with multiple SRFs in a SELECT list.
This syntax also provides a reasonable way to combine use of column
definition lists with WITH ORDINALITY: put the column definition list
inside TABLE(), where it's clear that it doesn't control the ordinality
column as well.
Also implement SQL-compliant multiple-argument UNNEST(), by turning
UNNEST(a,b,c) into TABLE(unnest(a), unnest(b), unnest(c)).
The SQL standard specifies TABLE() with only a single function, not
multiple functions, and it seems to require an implicit UNNEST() which is
not what this patch does. There may be something wrong with that reading
of the spec, though, because if it's right then the spec's TABLE() is just
a pointless alternative spelling of UNNEST(). After further review of
that, we might choose to adopt a different syntax for what this patch does,
but in any case this functionality seems clearly worthwhile.
Andrew Gierth, reviewed by Zoltán Böszörményi and Heikki Linnakangas, and
significantly revised by me
12 years ago
|
|
|
splan->functions =
|
|
|
|
fix_scan_list(root, splan->functions, rtoffset);
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
case T_TableFuncScan:
|
|
|
|
{
|
|
|
|
TableFuncScan *splan = (TableFuncScan *) plan;
|
|
|
|
|
|
|
|
splan->scan.scanrelid += rtoffset;
|
|
|
|
splan->scan.plan.targetlist =
|
|
|
|
fix_scan_list(root, splan->scan.plan.targetlist, rtoffset);
|
|
|
|
splan->scan.plan.qual =
|
|
|
|
fix_scan_list(root, splan->scan.plan.qual, rtoffset);
|
|
|
|
splan->tablefunc = (TableFunc *)
|
|
|
|
fix_scan_expr(root, (Node *) splan->tablefunc, rtoffset);
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
case T_ValuesScan:
|
|
|
|
{
|
|
|
|
ValuesScan *splan = (ValuesScan *) plan;
|
|
|
|
|
|
|
|
splan->scan.scanrelid += rtoffset;
|
|
|
|
splan->scan.plan.targetlist =
|
|
|
|
fix_scan_list(root, splan->scan.plan.targetlist, rtoffset);
|
|
|
|
splan->scan.plan.qual =
|
|
|
|
fix_scan_list(root, splan->scan.plan.qual, rtoffset);
|
|
|
|
splan->values_lists =
|
|
|
|
fix_scan_list(root, splan->values_lists, rtoffset);
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
case T_CteScan:
|
|
|
|
{
|
|
|
|
CteScan *splan = (CteScan *) plan;
|
|
|
|
|
|
|
|
splan->scan.scanrelid += rtoffset;
|
|
|
|
splan->scan.plan.targetlist =
|
|
|
|
fix_scan_list(root, splan->scan.plan.targetlist, rtoffset);
|
|
|
|
splan->scan.plan.qual =
|
|
|
|
fix_scan_list(root, splan->scan.plan.qual, rtoffset);
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
case T_NamedTuplestoreScan:
|
|
|
|
{
|
|
|
|
NamedTuplestoreScan *splan = (NamedTuplestoreScan *) plan;
|
|
|
|
|
|
|
|
splan->scan.scanrelid += rtoffset;
|
|
|
|
splan->scan.plan.targetlist =
|
|
|
|
fix_scan_list(root, splan->scan.plan.targetlist, rtoffset);
|
|
|
|
splan->scan.plan.qual =
|
|
|
|
fix_scan_list(root, splan->scan.plan.qual, rtoffset);
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
case T_WorkTableScan:
|
|
|
|
{
|
|
|
|
WorkTableScan *splan = (WorkTableScan *) plan;
|
|
|
|
|
|
|
|
splan->scan.scanrelid += rtoffset;
|
|
|
|
splan->scan.plan.targetlist =
|
|
|
|
fix_scan_list(root, splan->scan.plan.targetlist, rtoffset);
|
|
|
|
splan->scan.plan.qual =
|
|
|
|
fix_scan_list(root, splan->scan.plan.qual, rtoffset);
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
case T_ForeignScan:
|
|
|
|
set_foreignscan_references(root, (ForeignScan *) plan, rtoffset);
|
|
|
|
break;
|
|
|
|
case T_CustomScan:
|
|
|
|
set_customscan_references(root, (CustomScan *) plan, rtoffset);
|
|
|
|
break;
|
|
|
|
|
|
|
|
case T_NestLoop:
|
|
|
|
case T_MergeJoin:
|
|
|
|
case T_HashJoin:
|
|
|
|
set_join_references(root, (Join *) plan, rtoffset);
|
|
|
|
break;
|
|
|
|
|
|
|
|
case T_Gather:
|
|
|
|
case T_GatherMerge:
|
|
|
|
{
|
|
|
|
set_upper_references(root, plan, rtoffset);
|
|
|
|
set_param_references(root, plan);
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
|
|
|
|
case T_Hash:
|
|
|
|
case T_Material:
|
|
|
|
case T_Sort:
|
|
|
|
case T_Unique:
|
|
|
|
case T_SetOp:
|
|
|
|
|
|
|
|
/*
|
|
|
|
* These plan types don't actually bother to evaluate their
|
|
|
|
* targetlists, because they just return their unmodified input
|
|
|
|
* tuples. Even though the targetlist won't be used by the
|
|
|
|
* executor, we fix it up for possible use by EXPLAIN (not to
|
|
|
|
* mention ease of debugging --- wrong varnos are very confusing).
|
|
|
|
*/
|
|
|
|
set_dummy_tlist_references(plan, rtoffset);
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Since these plan types don't check quals either, we should not
|
|
|
|
* find any qual expression attached to them.
|
|
|
|
*/
|
|
|
|
Assert(plan->qual == NIL);
|
|
|
|
break;
|
|
|
|
case T_LockRows:
|
|
|
|
{
|
|
|
|
LockRows *splan = (LockRows *) plan;
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Like the plan types above, LockRows doesn't evaluate its
|
|
|
|
* tlist or quals. But we have to fix up the RT indexes in
|
|
|
|
* its rowmarks.
|
|
|
|
*/
|
|
|
|
set_dummy_tlist_references(plan, rtoffset);
|
|
|
|
Assert(splan->plan.qual == NIL);
|
|
|
|
|
|
|
|
foreach(l, splan->rowMarks)
|
|
|
|
{
|
Re-implement EvalPlanQual processing to improve its performance and eliminate
a lot of strange behaviors that occurred in join cases. We now identify the
"current" row for every joined relation in UPDATE, DELETE, and SELECT FOR
UPDATE/SHARE queries. If an EvalPlanQual recheck is necessary, we jam the
appropriate row into each scan node in the rechecking plan, forcing it to emit
only that one row. The former behavior could rescan the whole of each joined
relation for each recheck, which was terrible for performance, and what's much
worse could result in duplicated output tuples.
Also, the original implementation of EvalPlanQual could not re-use the recheck
execution tree --- it had to go through a full executor init and shutdown for
every row to be tested. To avoid this overhead, I've associated a special
runtime Param with each LockRows or ModifyTable plan node, and arranged to
make every scan node below such a node depend on that Param. Thus, by
signaling a change in that Param, the EPQ machinery can just rescan the
already-built test plan.
This patch also adds a prohibition on set-returning functions in the
targetlist of SELECT FOR UPDATE/SHARE. This is needed to avoid the
duplicate-output-tuple problem. It seems fairly reasonable since the
other restrictions on SELECT FOR UPDATE are meant to ensure that there
is a unique correspondence between source tuples and result tuples,
which an output SRF destroys as much as anything else does.
16 years ago
|
|
|
PlanRowMark *rc = (PlanRowMark *) lfirst(l);
|
|
|
|
|
|
|
|
rc->rti += rtoffset;
|
|
|
|
rc->prti += rtoffset;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
case T_Limit:
|
|
|
|
{
|
|
|
|
Limit *splan = (Limit *) plan;
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Like the plan types above, Limit doesn't evaluate its tlist
|
|
|
|
* or quals. It does have live expressions for limit/offset,
|
|
|
|
* however; and those cannot contain subplan variable refs, so
|
|
|
|
* fix_scan_expr works for them.
|
|
|
|
*/
|
|
|
|
set_dummy_tlist_references(plan, rtoffset);
|
|
|
|
Assert(splan->plan.qual == NIL);
|
|
|
|
|
|
|
|
splan->limitOffset =
|
|
|
|
fix_scan_expr(root, splan->limitOffset, rtoffset);
|
|
|
|
splan->limitCount =
|
|
|
|
fix_scan_expr(root, splan->limitCount, rtoffset);
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
case T_Agg:
|
|
|
|
{
|
|
|
|
Agg *agg = (Agg *) plan;
|
|
|
|
|
|
|
|
/*
|
|
|
|
* If this node is combining partial-aggregation results, we
|
|
|
|
* must convert its Aggrefs to contain references to the
|
|
|
|
* partial-aggregate subexpressions that will be available
|
|
|
|
* from the child plan node.
|
|
|
|
*/
|
|
|
|
if (DO_AGGSPLIT_COMBINE(agg->aggsplit))
|
|
|
|
{
|
|
|
|
plan->targetlist = (List *)
|
|
|
|
convert_combining_aggrefs((Node *) plan->targetlist,
|
|
|
|
NULL);
|
|
|
|
plan->qual = (List *)
|
|
|
|
convert_combining_aggrefs((Node *) plan->qual,
|
|
|
|
NULL);
|
|
|
|
}
|
|
|
|
|
|
|
|
set_upper_references(root, plan, rtoffset);
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
case T_Group:
|
|
|
|
set_upper_references(root, plan, rtoffset);
|
|
|
|
break;
|
|
|
|
case T_WindowAgg:
|
|
|
|
{
|
|
|
|
WindowAgg *wplan = (WindowAgg *) plan;
|
|
|
|
|
|
|
|
set_upper_references(root, plan, rtoffset);
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Like Limit node limit/offset expressions, WindowAgg has
|
|
|
|
* frame offset expressions, which cannot contain subplan
|
|
|
|
* variable refs, so fix_scan_expr works for them.
|
|
|
|
*/
|
|
|
|
wplan->startOffset =
|
|
|
|
fix_scan_expr(root, wplan->startOffset, rtoffset);
|
|
|
|
wplan->endOffset =
|
|
|
|
fix_scan_expr(root, wplan->endOffset, rtoffset);
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
case T_Result:
|
|
|
|
{
|
|
|
|
Result *splan = (Result *) plan;
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Result may or may not have a subplan; if not, it's more
|
|
|
|
* like a scan node than an upper node.
|
|
|
|
*/
|
|
|
|
if (splan->plan.lefttree != NULL)
|
|
|
|
set_upper_references(root, plan, rtoffset);
|
|
|
|
else
|
|
|
|
{
|
|
|
|
splan->plan.targetlist =
|
|
|
|
fix_scan_list(root, splan->plan.targetlist, rtoffset);
|
|
|
|
splan->plan.qual =
|
|
|
|
fix_scan_list(root, splan->plan.qual, rtoffset);
|
|
|
|
}
|
|
|
|
/* resconstantqual can't contain any subplan variable refs */
|
|
|
|
splan->resconstantqual =
|
|
|
|
fix_scan_expr(root, splan->resconstantqual, rtoffset);
|
|
|
|
}
|
|
|
|
break;
|
Move targetlist SRF handling from expression evaluation to new executor node.
Evaluation of set returning functions (SRFs_ in the targetlist (like SELECT
generate_series(1,5)) so far was done in the expression evaluation (i.e.
ExecEvalExpr()) and projection (i.e. ExecProject/ExecTargetList) code.
This meant that most executor nodes performing projection, and most
expression evaluation functions, had to deal with the possibility that an
evaluated expression could return a set of return values.
That's bad because it leads to repeated code in a lot of places. It also,
and that's my (Andres's) motivation, made it a lot harder to implement a
more efficient way of doing expression evaluation.
To fix this, introduce a new executor node (ProjectSet) that can evaluate
targetlists containing one or more SRFs. To avoid the complexity of the old
way of handling nested expressions returning sets (e.g. having to pass up
ExprDoneCond, and dealing with arguments to functions returning sets etc.),
those SRFs can only be at the top level of the node's targetlist. The
planner makes sure (via split_pathtarget_at_srfs()) that SRF evaluation is
only necessary in ProjectSet nodes and that SRFs are only present at the
top level of the node's targetlist. If there are nested SRFs the planner
creates multiple stacked ProjectSet nodes. The ProjectSet nodes always get
input from an underlying node.
We also discussed and prototyped evaluating targetlist SRFs using ROWS
FROM(), but that turned out to be more complicated than we'd hoped.
While moving SRF evaluation to ProjectSet would allow to retain the old
"least common multiple" behavior when multiple SRFs are present in one
targetlist (i.e. continue returning rows until all SRFs are at the end of
their input at the same time), we decided to instead only return rows till
all SRFs are exhausted, returning NULL for already exhausted ones. We
deemed the previous behavior to be too confusing, unexpected and actually
not particularly useful.
As a side effect, the previously prohibited case of multiple set returning
arguments to a function, is now allowed. Not because it's particularly
desirable, but because it ends up working and there seems to be no argument
for adding code to prohibit it.
Currently the behavior for COALESCE and CASE containing SRFs has changed,
returning multiple rows from the expression, even when the SRF containing
"arm" of the expression is not evaluated. That's because the SRFs are
evaluated in a separate ProjectSet node. As that's quite confusing, we're
likely to instead prohibit SRFs in those places. But that's still being
discussed, and the code would reside in places not touched here, so that's
a task for later.
There's a lot of, now superfluous, code dealing with set return expressions
around. But as the changes to get rid of those are verbose largely boring,
it seems better for readability to keep the cleanup as a separate commit.
Author: Tom Lane and Andres Freund
Discussion: https://postgr.es/m/20160822214023.aaxz5l4igypowyri@alap3.anarazel.de
9 years ago
|
|
|
case T_ProjectSet:
|
|
|
|
set_upper_references(root, plan, rtoffset);
|
|
|
|
break;
|
|
|
|
case T_ModifyTable:
|
|
|
|
{
|
|
|
|
ModifyTable *splan = (ModifyTable *) plan;
|
|
|
|
|
Fix planner's handling of RETURNING lists in writable CTEs.
setrefs.c failed to do "rtoffset" adjustment of Vars in RETURNING lists,
which meant they were left with the wrong varnos when the RETURNING list
was in a subquery. That was never possible before writable CTEs, of
course, but now it's broken. The executor fails to notice any problem
because ExecEvalVar just references the ecxt_scantuple for any normal
varno; but EXPLAIN breaks when the varno is wrong, as illustrated in a
recent complaint from Bartosz Dmytrak.
Since the eventual rtoffset of the subquery is not known at the time
we are preparing its plan node, the previous scheme of executing
set_returning_clause_references() at that time cannot handle this
adjustment. Fortunately, it turns out that we don't really need to do it
that way, because all the needed information is available during normal
setrefs.c execution; we just have to dig it out of the ModifyTable node.
So, do that, and get rid of the kluge of early setrefs processing of
RETURNING lists. (This is a little bit of a cheat in the case of inherited
UPDATE/DELETE, because we are not passing a "root" struct that corresponds
exactly to what the subplan was built with. But that doesn't matter, and
anyway this is less ugly than early setrefs processing was.)
Back-patch to 9.1, where the problem became possible to hit.
14 years ago
|
|
|
Assert(splan->plan.targetlist == NIL);
|
|
|
|
Assert(splan->plan.qual == NIL);
|
|
|
|
|
|
|
|
splan->withCheckOptionLists =
|
|
|
|
fix_scan_list(root, splan->withCheckOptionLists, rtoffset);
|
|
|
|
|
Fix planner's handling of RETURNING lists in writable CTEs.
setrefs.c failed to do "rtoffset" adjustment of Vars in RETURNING lists,
which meant they were left with the wrong varnos when the RETURNING list
was in a subquery. That was never possible before writable CTEs, of
course, but now it's broken. The executor fails to notice any problem
because ExecEvalVar just references the ecxt_scantuple for any normal
varno; but EXPLAIN breaks when the varno is wrong, as illustrated in a
recent complaint from Bartosz Dmytrak.
Since the eventual rtoffset of the subquery is not known at the time
we are preparing its plan node, the previous scheme of executing
set_returning_clause_references() at that time cannot handle this
adjustment. Fortunately, it turns out that we don't really need to do it
that way, because all the needed information is available during normal
setrefs.c execution; we just have to dig it out of the ModifyTable node.
So, do that, and get rid of the kluge of early setrefs processing of
RETURNING lists. (This is a little bit of a cheat in the case of inherited
UPDATE/DELETE, because we are not passing a "root" struct that corresponds
exactly to what the subplan was built with. But that doesn't matter, and
anyway this is less ugly than early setrefs processing was.)
Back-patch to 9.1, where the problem became possible to hit.
14 years ago
|
|
|
if (splan->returningLists)
|
|
|
|
{
|
|
|
|
List *newRL = NIL;
|
|
|
|
ListCell *lcrl,
|
|
|
|
*lcrr,
|
|
|
|
*lcp;
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Pass each per-subplan returningList through
|
|
|
|
* set_returning_clause_references().
|
|
|
|
*/
|
|
|
|
Assert(list_length(splan->returningLists) == list_length(splan->resultRelations));
|
|
|
|
Assert(list_length(splan->returningLists) == list_length(splan->plans));
|
|
|
|
forthree(lcrl, splan->returningLists,
|
|
|
|
lcrr, splan->resultRelations,
|
|
|
|
lcp, splan->plans)
|
|
|
|
{
|
|
|
|
List *rlist = (List *) lfirst(lcrl);
|
|
|
|
Index resultrel = lfirst_int(lcrr);
|
|
|
|
Plan *subplan = (Plan *) lfirst(lcp);
|
Fix planner's handling of RETURNING lists in writable CTEs.
setrefs.c failed to do "rtoffset" adjustment of Vars in RETURNING lists,
which meant they were left with the wrong varnos when the RETURNING list
was in a subquery. That was never possible before writable CTEs, of
course, but now it's broken. The executor fails to notice any problem
because ExecEvalVar just references the ecxt_scantuple for any normal
varno; but EXPLAIN breaks when the varno is wrong, as illustrated in a
recent complaint from Bartosz Dmytrak.
Since the eventual rtoffset of the subquery is not known at the time
we are preparing its plan node, the previous scheme of executing
set_returning_clause_references() at that time cannot handle this
adjustment. Fortunately, it turns out that we don't really need to do it
that way, because all the needed information is available during normal
setrefs.c execution; we just have to dig it out of the ModifyTable node.
So, do that, and get rid of the kluge of early setrefs processing of
RETURNING lists. (This is a little bit of a cheat in the case of inherited
UPDATE/DELETE, because we are not passing a "root" struct that corresponds
exactly to what the subplan was built with. But that doesn't matter, and
anyway this is less ugly than early setrefs processing was.)
Back-patch to 9.1, where the problem became possible to hit.
14 years ago
|
|
|
|
|
|
|
rlist = set_returning_clause_references(root,
|
|
|
|
rlist,
|
|
|
|
subplan,
|
|
|
|
resultrel,
|
|
|
|
rtoffset);
|
|
|
|
newRL = lappend(newRL, rlist);
|
|
|
|
}
|
|
|
|
splan->returningLists = newRL;
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Set up the visible plan targetlist as being the same as
|
|
|
|
* the first RETURNING list. This is for the use of
|
|
|
|
* EXPLAIN; the executor won't pay any attention to the
|
|
|
|
* targetlist. We postpone this step until here so that
|
Fix planner's handling of RETURNING lists in writable CTEs.
setrefs.c failed to do "rtoffset" adjustment of Vars in RETURNING lists,
which meant they were left with the wrong varnos when the RETURNING list
was in a subquery. That was never possible before writable CTEs, of
course, but now it's broken. The executor fails to notice any problem
because ExecEvalVar just references the ecxt_scantuple for any normal
varno; but EXPLAIN breaks when the varno is wrong, as illustrated in a
recent complaint from Bartosz Dmytrak.
Since the eventual rtoffset of the subquery is not known at the time
we are preparing its plan node, the previous scheme of executing
set_returning_clause_references() at that time cannot handle this
adjustment. Fortunately, it turns out that we don't really need to do it
that way, because all the needed information is available during normal
setrefs.c execution; we just have to dig it out of the ModifyTable node.
So, do that, and get rid of the kluge of early setrefs processing of
RETURNING lists. (This is a little bit of a cheat in the case of inherited
UPDATE/DELETE, because we are not passing a "root" struct that corresponds
exactly to what the subplan was built with. But that doesn't matter, and
anyway this is less ugly than early setrefs processing was.)
Back-patch to 9.1, where the problem became possible to hit.
14 years ago
|
|
|
* we don't have to do set_returning_clause_references()
|
|
|
|
* twice on identical targetlists.
|
|
|
|
*/
|
|
|
|
splan->plan.targetlist = copyObject(linitial(newRL));
|
|
|
|
}
|
|
|
|
|
Add support for INSERT ... ON CONFLICT DO NOTHING/UPDATE.
The newly added ON CONFLICT clause allows to specify an alternative to
raising a unique or exclusion constraint violation error when inserting.
ON CONFLICT refers to constraints that can either be specified using a
inference clause (by specifying the columns of a unique constraint) or
by naming a unique or exclusion constraint. DO NOTHING avoids the
constraint violation, without touching the pre-existing row. DO UPDATE
SET ... [WHERE ...] updates the pre-existing tuple, and has access to
both the tuple proposed for insertion and the existing tuple; the
optional WHERE clause can be used to prevent an update from being
executed. The UPDATE SET and WHERE clauses have access to the tuple
proposed for insertion using the "magic" EXCLUDED alias, and to the
pre-existing tuple using the table name or its alias.
This feature is often referred to as upsert.
This is implemented using a new infrastructure called "speculative
insertion". It is an optimistic variant of regular insertion that first
does a pre-check for existing tuples and then attempts an insert. If a
violating tuple was inserted concurrently, the speculatively inserted
tuple is deleted and a new attempt is made. If the pre-check finds a
matching tuple the alternative DO NOTHING or DO UPDATE action is taken.
If the insertion succeeds without detecting a conflict, the tuple is
deemed inserted.
To handle the possible ambiguity between the excluded alias and a table
named excluded, and for convenience with long relation names, INSERT
INTO now can alias its target table.
Bumps catversion as stored rules change.
Author: Peter Geoghegan, with significant contributions from Heikki
Linnakangas and Andres Freund. Testing infrastructure by Jeff Janes.
Reviewed-By: Heikki Linnakangas, Andres Freund, Robert Haas, Simon Riggs,
Dean Rasheed, Stephen Frost and many others.
11 years ago
|
|
|
/*
|
|
|
|
* We treat ModifyTable with ON CONFLICT as a form of 'pseudo
|
|
|
|
* join', where the inner side is the EXCLUDED tuple.
|
|
|
|
* Therefore use fix_join_expr to setup the relevant variables
|
|
|
|
* to INNER_VAR. We explicitly don't create any OUTER_VARs as
|
Add support for INSERT ... ON CONFLICT DO NOTHING/UPDATE.
The newly added ON CONFLICT clause allows to specify an alternative to
raising a unique or exclusion constraint violation error when inserting.
ON CONFLICT refers to constraints that can either be specified using a
inference clause (by specifying the columns of a unique constraint) or
by naming a unique or exclusion constraint. DO NOTHING avoids the
constraint violation, without touching the pre-existing row. DO UPDATE
SET ... [WHERE ...] updates the pre-existing tuple, and has access to
both the tuple proposed for insertion and the existing tuple; the
optional WHERE clause can be used to prevent an update from being
executed. The UPDATE SET and WHERE clauses have access to the tuple
proposed for insertion using the "magic" EXCLUDED alias, and to the
pre-existing tuple using the table name or its alias.
This feature is often referred to as upsert.
This is implemented using a new infrastructure called "speculative
insertion". It is an optimistic variant of regular insertion that first
does a pre-check for existing tuples and then attempts an insert. If a
violating tuple was inserted concurrently, the speculatively inserted
tuple is deleted and a new attempt is made. If the pre-check finds a
matching tuple the alternative DO NOTHING or DO UPDATE action is taken.
If the insertion succeeds without detecting a conflict, the tuple is
deemed inserted.
To handle the possible ambiguity between the excluded alias and a table
named excluded, and for convenience with long relation names, INSERT
INTO now can alias its target table.
Bumps catversion as stored rules change.
Author: Peter Geoghegan, with significant contributions from Heikki
Linnakangas and Andres Freund. Testing infrastructure by Jeff Janes.
Reviewed-By: Heikki Linnakangas, Andres Freund, Robert Haas, Simon Riggs,
Dean Rasheed, Stephen Frost and many others.
11 years ago
|
|
|
* those are already used by RETURNING and it seems better to
|
|
|
|
* be non-conflicting.
|
|
|
|
*/
|
|
|
|
if (splan->onConflictSet)
|
|
|
|
{
|
|
|
|
indexed_tlist *itlist;
|
|
|
|
|
|
|
|
itlist = build_tlist_index(splan->exclRelTlist);
|
|
|
|
|
|
|
|
splan->onConflictSet =
|
|
|
|
fix_join_expr(root, splan->onConflictSet,
|
|
|
|
NULL, itlist,
|
|
|
|
linitial_int(splan->resultRelations),
|
|
|
|
rtoffset);
|
|
|
|
|
|
|
|
splan->onConflictWhere = (Node *)
|
|
|
|
fix_join_expr(root, (List *) splan->onConflictWhere,
|
|
|
|
NULL, itlist,
|
|
|
|
linitial_int(splan->resultRelations),
|
|
|
|
rtoffset);
|
|
|
|
|
|
|
|
pfree(itlist);
|
|
|
|
|
|
|
|
splan->exclRelTlist =
|
|
|
|
fix_scan_list(root, splan->exclRelTlist, rtoffset);
|
Add support for INSERT ... ON CONFLICT DO NOTHING/UPDATE.
The newly added ON CONFLICT clause allows to specify an alternative to
raising a unique or exclusion constraint violation error when inserting.
ON CONFLICT refers to constraints that can either be specified using a
inference clause (by specifying the columns of a unique constraint) or
by naming a unique or exclusion constraint. DO NOTHING avoids the
constraint violation, without touching the pre-existing row. DO UPDATE
SET ... [WHERE ...] updates the pre-existing tuple, and has access to
both the tuple proposed for insertion and the existing tuple; the
optional WHERE clause can be used to prevent an update from being
executed. The UPDATE SET and WHERE clauses have access to the tuple
proposed for insertion using the "magic" EXCLUDED alias, and to the
pre-existing tuple using the table name or its alias.
This feature is often referred to as upsert.
This is implemented using a new infrastructure called "speculative
insertion". It is an optimistic variant of regular insertion that first
does a pre-check for existing tuples and then attempts an insert. If a
violating tuple was inserted concurrently, the speculatively inserted
tuple is deleted and a new attempt is made. If the pre-check finds a
matching tuple the alternative DO NOTHING or DO UPDATE action is taken.
If the insertion succeeds without detecting a conflict, the tuple is
deemed inserted.
To handle the possible ambiguity between the excluded alias and a table
named excluded, and for convenience with long relation names, INSERT
INTO now can alias its target table.
Bumps catversion as stored rules change.
Author: Peter Geoghegan, with significant contributions from Heikki
Linnakangas and Andres Freund. Testing infrastructure by Jeff Janes.
Reviewed-By: Heikki Linnakangas, Andres Freund, Robert Haas, Simon Riggs,
Dean Rasheed, Stephen Frost and many others.
11 years ago
|
|
|
}
|
|
|
|
|
|
|
|
splan->nominalRelation += rtoffset;
|
Add support for INSERT ... ON CONFLICT DO NOTHING/UPDATE.
The newly added ON CONFLICT clause allows to specify an alternative to
raising a unique or exclusion constraint violation error when inserting.
ON CONFLICT refers to constraints that can either be specified using a
inference clause (by specifying the columns of a unique constraint) or
by naming a unique or exclusion constraint. DO NOTHING avoids the
constraint violation, without touching the pre-existing row. DO UPDATE
SET ... [WHERE ...] updates the pre-existing tuple, and has access to
both the tuple proposed for insertion and the existing tuple; the
optional WHERE clause can be used to prevent an update from being
executed. The UPDATE SET and WHERE clauses have access to the tuple
proposed for insertion using the "magic" EXCLUDED alias, and to the
pre-existing tuple using the table name or its alias.
This feature is often referred to as upsert.
This is implemented using a new infrastructure called "speculative
insertion". It is an optimistic variant of regular insertion that first
does a pre-check for existing tuples and then attempts an insert. If a
violating tuple was inserted concurrently, the speculatively inserted
tuple is deleted and a new attempt is made. If the pre-check finds a
matching tuple the alternative DO NOTHING or DO UPDATE action is taken.
If the insertion succeeds without detecting a conflict, the tuple is
deemed inserted.
To handle the possible ambiguity between the excluded alias and a table
named excluded, and for convenience with long relation names, INSERT
INTO now can alias its target table.
Bumps catversion as stored rules change.
Author: Peter Geoghegan, with significant contributions from Heikki
Linnakangas and Andres Freund. Testing infrastructure by Jeff Janes.
Reviewed-By: Heikki Linnakangas, Andres Freund, Robert Haas, Simon Riggs,
Dean Rasheed, Stephen Frost and many others.
11 years ago
|
|
|
splan->exclRelRTI += rtoffset;
|
|
|
|
|
|
|
|
foreach(l, splan->partitioned_rels)
|
|
|
|
{
|
|
|
|
lfirst_int(l) += rtoffset;
|
|
|
|
}
|
|
|
|
foreach(l, splan->resultRelations)
|
|
|
|
{
|
|
|
|
lfirst_int(l) += rtoffset;
|
|
|
|
}
|
Re-implement EvalPlanQual processing to improve its performance and eliminate
a lot of strange behaviors that occurred in join cases. We now identify the
"current" row for every joined relation in UPDATE, DELETE, and SELECT FOR
UPDATE/SHARE queries. If an EvalPlanQual recheck is necessary, we jam the
appropriate row into each scan node in the rechecking plan, forcing it to emit
only that one row. The former behavior could rescan the whole of each joined
relation for each recheck, which was terrible for performance, and what's much
worse could result in duplicated output tuples.
Also, the original implementation of EvalPlanQual could not re-use the recheck
execution tree --- it had to go through a full executor init and shutdown for
every row to be tested. To avoid this overhead, I've associated a special
runtime Param with each LockRows or ModifyTable plan node, and arranged to
make every scan node below such a node depend on that Param. Thus, by
signaling a change in that Param, the EPQ machinery can just rescan the
already-built test plan.
This patch also adds a prohibition on set-returning functions in the
targetlist of SELECT FOR UPDATE/SHARE. This is needed to avoid the
duplicate-output-tuple problem. It seems fairly reasonable since the
other restrictions on SELECT FOR UPDATE are meant to ensure that there
is a unique correspondence between source tuples and result tuples,
which an output SRF destroys as much as anything else does.
16 years ago
|
|
|
foreach(l, splan->rowMarks)
|
|
|
|
{
|
|
|
|
PlanRowMark *rc = (PlanRowMark *) lfirst(l);
|
|
|
|
|
|
|
|
rc->rti += rtoffset;
|
|
|
|
rc->prti += rtoffset;
|
|
|
|
}
|
|
|
|
foreach(l, splan->plans)
|
|
|
|
{
|
|
|
|
lfirst(l) = set_plan_refs(root,
|
|
|
|
(Plan *) lfirst(l),
|
|
|
|
rtoffset);
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Append this ModifyTable node's final result relation RT
|
|
|
|
* index(es) to the global list for the plan, and set its
|
|
|
|
* resultRelIndex to reflect their starting position in the
|
|
|
|
* global list.
|
|
|
|
*/
|
|
|
|
splan->resultRelIndex = list_length(root->glob->resultRelations);
|
|
|
|
root->glob->resultRelations =
|
|
|
|
list_concat(root->glob->resultRelations,
|
|
|
|
list_copy(splan->resultRelations));
|
|
|
|
|
|
|
|
/*
|
|
|
|
* If the main target relation is a partitioned table, the
|
|
|
|
* following list contains the RT indexes of partitioned child
|
|
|
|
* relations including the root, which are not included in the
|
|
|
|
* above list. We also keep RT indexes of the roots
|
|
|
|
* separately to be identified as such during the executor
|
|
|
|
* initialization.
|
|
|
|
*/
|
|
|
|
if (splan->partitioned_rels != NIL)
|
|
|
|
{
|
|
|
|
root->glob->nonleafResultRelations =
|
|
|
|
list_concat(root->glob->nonleafResultRelations,
|
|
|
|
list_copy(splan->partitioned_rels));
|
|
|
|
/* Remember where this root will be in the global list. */
|
|
|
|
splan->rootResultRelIndex =
|
|
|
|
list_length(root->glob->rootResultRelations);
|
|
|
|
root->glob->rootResultRelations =
|
|
|
|
lappend_int(root->glob->rootResultRelations,
|
|
|
|
linitial_int(splan->partitioned_rels));
|
|
|
|
}
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
case T_Append:
|
|
|
|
{
|
|
|
|
Append *splan = (Append *) plan;
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Append, like Sort et al, doesn't actually evaluate its
|
|
|
|
* targetlist or check quals.
|
|
|
|
*/
|
|
|
|
set_dummy_tlist_references(plan, rtoffset);
|
|
|
|
Assert(splan->plan.qual == NIL);
|
|
|
|
foreach(l, splan->partitioned_rels)
|
|
|
|
{
|
|
|
|
lfirst_int(l) += rtoffset;
|
|
|
|
}
|
|
|
|
foreach(l, splan->appendplans)
|
|
|
|
{
|
|
|
|
lfirst(l) = set_plan_refs(root,
|
|
|
|
(Plan *) lfirst(l),
|
|
|
|
rtoffset);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
case T_MergeAppend:
|
|
|
|
{
|
|
|
|
MergeAppend *splan = (MergeAppend *) plan;
|
|
|
|
|
|
|
|
/*
|
|
|
|
* MergeAppend, like Sort et al, doesn't actually evaluate its
|
|
|
|
* targetlist or check quals.
|
|
|
|
*/
|
|
|
|
set_dummy_tlist_references(plan, rtoffset);
|
|
|
|
Assert(splan->plan.qual == NIL);
|
|
|
|
foreach(l, splan->partitioned_rels)
|
|
|
|
{
|
|
|
|
lfirst_int(l) += rtoffset;
|
|
|
|
}
|
|
|
|
foreach(l, splan->mergeplans)
|
|
|
|
{
|
|
|
|
lfirst(l) = set_plan_refs(root,
|
|
|
|
(Plan *) lfirst(l),
|
|
|
|
rtoffset);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
case T_RecursiveUnion:
|
|
|
|
/* This doesn't evaluate targetlist or check quals either */
|
|
|
|
set_dummy_tlist_references(plan, rtoffset);
|
|
|
|
Assert(plan->qual == NIL);
|
|
|
|
break;
|
|
|
|
case T_BitmapAnd:
|
|
|
|
{
|
|
|
|
BitmapAnd *splan = (BitmapAnd *) plan;
|
|
|
|
|
|
|
|
/* BitmapAnd works like Append, but has no tlist */
|
|
|
|
Assert(splan->plan.targetlist == NIL);
|
|
|
|
Assert(splan->plan.qual == NIL);
|
|
|
|
foreach(l, splan->bitmapplans)
|
|
|
|
{
|
|
|
|
lfirst(l) = set_plan_refs(root,
|
|
|
|
(Plan *) lfirst(l),
|
|
|
|
rtoffset);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
case T_BitmapOr:
|
|
|
|
{
|
|
|
|
BitmapOr *splan = (BitmapOr *) plan;
|
|
|
|
|
|
|
|
/* BitmapOr works like Append, but has no tlist */
|
|
|
|
Assert(splan->plan.targetlist == NIL);
|
|
|
|
Assert(splan->plan.qual == NIL);
|
|
|
|
foreach(l, splan->bitmapplans)
|
|
|
|
{
|
|
|
|
lfirst(l) = set_plan_refs(root,
|
|
|
|
(Plan *) lfirst(l),
|
|
|
|
rtoffset);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
default:
|
|
|
|
elog(ERROR, "unrecognized node type: %d",
|
|
|
|
(int) nodeTag(plan));
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Now recurse into child plans, if any
|
|
|
|
*
|
|
|
|
* NOTE: it is essential that we recurse into child plans AFTER we set
|
|
|
|
* subplan references in this plan's tlist and quals. If we did the
|
|
|
|
* reference-adjustments bottom-up, then we would fail to match this
|
|
|
|
* plan's var nodes against the already-modified nodes of the children.
|
|
|
|
*/
|
|
|
|
plan->lefttree = set_plan_refs(root, plan->lefttree, rtoffset);
|
|
|
|
plan->righttree = set_plan_refs(root, plan->righttree, rtoffset);
|
|
|
|
|
|
|
|
return plan;
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* set_indexonlyscan_references
|
|
|
|
* Do set_plan_references processing on an IndexOnlyScan
|
|
|
|
*
|
|
|
|
* This is unlike the handling of a plain IndexScan because we have to
|
|
|
|
* convert Vars referencing the heap into Vars referencing the index.
|
|
|
|
* We can use the fix_upper_expr machinery for that, by working from a
|
|
|
|
* targetlist describing the index columns.
|
|
|
|
*/
|
|
|
|
static Plan *
|
|
|
|
set_indexonlyscan_references(PlannerInfo *root,
|
|
|
|
IndexOnlyScan *plan,
|
|
|
|
int rtoffset)
|
|
|
|
{
|
|
|
|
indexed_tlist *index_itlist;
|
|
|
|
|
|
|
|
index_itlist = build_tlist_index(plan->indextlist);
|
|
|
|
|
|
|
|
plan->scan.scanrelid += rtoffset;
|
|
|
|
plan->scan.plan.targetlist = (List *)
|
|
|
|
fix_upper_expr(root,
|
|
|
|
(Node *) plan->scan.plan.targetlist,
|
|
|
|
index_itlist,
|
|
|
|
INDEX_VAR,
|
|
|
|
rtoffset);
|
|
|
|
plan->scan.plan.qual = (List *)
|
|
|
|
fix_upper_expr(root,
|
|
|
|
(Node *) plan->scan.plan.qual,
|
|
|
|
index_itlist,
|
|
|
|
INDEX_VAR,
|
|
|
|
rtoffset);
|
|
|
|
/* indexqual is already transformed to reference index columns */
|
|
|
|
plan->indexqual = fix_scan_list(root, plan->indexqual, rtoffset);
|
|
|
|
/* indexorderby is already transformed to reference index columns */
|
|
|
|
plan->indexorderby = fix_scan_list(root, plan->indexorderby, rtoffset);
|
|
|
|
/* indextlist must NOT be transformed to reference index columns */
|
|
|
|
plan->indextlist = fix_scan_list(root, plan->indextlist, rtoffset);
|
|
|
|
|
|
|
|
pfree(index_itlist);
|
|
|
|
|
|
|
|
return (Plan *) plan;
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* set_subqueryscan_references
|
|
|
|
* Do set_plan_references processing on a SubqueryScan
|
|
|
|
*
|
|
|
|
* We try to strip out the SubqueryScan entirely; if we can't, we have
|
|
|
|
* to do the normal processing on it.
|
|
|
|
*/
|
|
|
|
static Plan *
|
|
|
|
set_subqueryscan_references(PlannerInfo *root,
|
|
|
|
SubqueryScan *plan,
|
|
|
|
int rtoffset)
|
|
|
|
{
|
|
|
|
RelOptInfo *rel;
|
|
|
|
Plan *result;
|
|
|
|
|
|
|
|
/* Need to look up the subquery's RelOptInfo, since we need its subroot */
|
|
|
|
rel = find_base_rel(root, plan->scan.scanrelid);
|
|
|
|
|
|
|
|
/* Recursively process the subplan */
|
|
|
|
plan->subplan = set_plan_references(rel->subroot, plan->subplan);
|
|
|
|
|
|
|
|
if (trivial_subqueryscan(plan))
|
|
|
|
{
|
|
|
|
/*
|
|
|
|
* We can omit the SubqueryScan node and just pull up the subplan.
|
|
|
|
*/
|
|
|
|
ListCell *lp,
|
|
|
|
*lc;
|
|
|
|
|
|
|
|
result = plan->subplan;
|
|
|
|
|
|
|
|
/* We have to be sure we don't lose any initplans */
|
|
|
|
result->initPlan = list_concat(plan->scan.plan.initPlan,
|
|
|
|
result->initPlan);
|
|
|
|
|
|
|
|
/*
|
|
|
|
* We also have to transfer the SubqueryScan's result-column names
|
|
|
|
* into the subplan, else columns sent to client will be improperly
|
|
|
|
* labeled if this is the topmost plan level. Copy the "source
|
|
|
|
* column" information too.
|
|
|
|
*/
|
|
|
|
forboth(lp, plan->scan.plan.targetlist, lc, result->targetlist)
|
|
|
|
{
|
|
|
|
TargetEntry *ptle = (TargetEntry *) lfirst(lp);
|
|
|
|
TargetEntry *ctle = (TargetEntry *) lfirst(lc);
|
|
|
|
|
|
|
|
ctle->resname = ptle->resname;
|
|
|
|
ctle->resorigtbl = ptle->resorigtbl;
|
|
|
|
ctle->resorigcol = ptle->resorigcol;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
/*
|
|
|
|
* Keep the SubqueryScan node. We have to do the processing that
|
|
|
|
* set_plan_references would otherwise have done on it. Notice we do
|
|
|
|
* not do set_upper_references() here, because a SubqueryScan will
|
|
|
|
* always have been created with correct references to its subplan's
|
|
|
|
* outputs to begin with.
|
|
|
|
*/
|
|
|
|
plan->scan.scanrelid += rtoffset;
|
|
|
|
plan->scan.plan.targetlist =
|
|
|
|
fix_scan_list(root, plan->scan.plan.targetlist, rtoffset);
|
|
|
|
plan->scan.plan.qual =
|
|
|
|
fix_scan_list(root, plan->scan.plan.qual, rtoffset);
|
|
|
|
|
|
|
|
result = (Plan *) plan;
|
|
|
|
}
|
|
|
|
|
|
|
|
return result;
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* trivial_subqueryscan
|
|
|
|
* Detect whether a SubqueryScan can be deleted from the plan tree.
|
|
|
|
*
|
|
|
|
* We can delete it if it has no qual to check and the targetlist just
|
|
|
|
* regurgitates the output of the child plan.
|
|
|
|
*/
|
|
|
|
static bool
|
|
|
|
trivial_subqueryscan(SubqueryScan *plan)
|
|
|
|
{
|
|
|
|
int attrno;
|
|
|
|
ListCell *lp,
|
|
|
|
*lc;
|
|
|
|
|
|
|
|
if (plan->scan.plan.qual != NIL)
|
|
|
|
return false;
|
|
|
|
|
|
|
|
if (list_length(plan->scan.plan.targetlist) !=
|
|
|
|
list_length(plan->subplan->targetlist))
|
|
|
|
return false; /* tlists not same length */
|
|
|
|
|
|
|
|
attrno = 1;
|
|
|
|
forboth(lp, plan->scan.plan.targetlist, lc, plan->subplan->targetlist)
|
|
|
|
{
|
|
|
|
TargetEntry *ptle = (TargetEntry *) lfirst(lp);
|
|
|
|
TargetEntry *ctle = (TargetEntry *) lfirst(lc);
|
|
|
|
|
|
|
|
if (ptle->resjunk != ctle->resjunk)
|
|
|
|
return false; /* tlist doesn't match junk status */
|
|
|
|
|
|
|
|
/*
|
|
|
|
* We accept either a Var referencing the corresponding element of the
|
|
|
|
* subplan tlist, or a Const equaling the subplan element. See
|
|
|
|
* generate_setop_tlist() for motivation.
|
|
|
|
*/
|
|
|
|
if (ptle->expr && IsA(ptle->expr, Var))
|
|
|
|
{
|
|
|
|
Var *var = (Var *) ptle->expr;
|
|
|
|
|
|
|
|
Assert(var->varno == plan->scan.scanrelid);
|
|
|
|
Assert(var->varlevelsup == 0);
|
|
|
|
if (var->varattno != attrno)
|
|
|
|
return false; /* out of order */
|
|
|
|
}
|
|
|
|
else if (ptle->expr && IsA(ptle->expr, Const))
|
|
|
|
{
|
|
|
|
if (!equal(ptle->expr, ctle->expr))
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
else
|
|
|
|
return false;
|
|
|
|
|
|
|
|
attrno++;
|
|
|
|
}
|
|
|
|
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
Code review for foreign/custom join pushdown patch.
Commit e7cb7ee14555cc9c5773e2c102efd6371f6f2005 included some design
decisions that seem pretty questionable to me, and there was quite a lot
of stuff not to like about the documentation and comments. Clean up
as follows:
* Consider foreign joins only between foreign tables on the same server,
rather than between any two foreign tables with the same underlying FDW
handler function. In most if not all cases, the FDW would simply have had
to apply the same-server restriction itself (far more expensively, both for
lack of caching and because it would be repeated for each combination of
input sub-joins), or else risk nasty bugs. Anyone who's really intent on
doing something outside this restriction can always use the
set_join_pathlist_hook.
* Rename fdw_ps_tlist/custom_ps_tlist to fdw_scan_tlist/custom_scan_tlist
to better reflect what they're for, and allow these custom scan tlists
to be used even for base relations.
* Change make_foreignscan() API to include passing the fdw_scan_tlist
value, since the FDW is required to set that. Backwards compatibility
doesn't seem like an adequate reason to expect FDWs to set it in some
ad-hoc extra step, and anyway existing FDWs can just pass NIL.
* Change the API of path-generating subroutines of add_paths_to_joinrel,
and in particular that of GetForeignJoinPaths and set_join_pathlist_hook,
so that various less-used parameters are passed in a struct rather than
as separate parameter-list entries. The objective here is to reduce the
probability that future additions to those parameter lists will result in
source-level API breaks for users of these hooks. It's possible that this
is even a small win for the core code, since most CPU architectures can't
pass more than half a dozen parameters efficiently anyway. I kept root,
joinrel, outerrel, innerrel, and jointype as separate parameters to reduce
code churn in joinpath.c --- in particular, putting jointype into the
struct would have been problematic because of the subroutines' habit of
changing their local copies of that variable.
* Avoid ad-hocery in ExecAssignScanProjectionInfo. It was probably all
right for it to know about IndexOnlyScan, but if the list is to grow
we should refactor the knowledge out to the callers.
* Restore nodeForeignscan.c's previous use of the relcache to avoid
extra GetFdwRoutine lookups for base-relation scans.
* Lots of cleanup of documentation and missed comments. Re-order some
code additions into more logical places.
10 years ago
|
|
|
/*
|
|
|
|
* set_foreignscan_references
|
|
|
|
* Do set_plan_references processing on a ForeignScan
|
|
|
|
*/
|
|
|
|
static void
|
|
|
|
set_foreignscan_references(PlannerInfo *root,
|
|
|
|
ForeignScan *fscan,
|
|
|
|
int rtoffset)
|
|
|
|
{
|
|
|
|
/* Adjust scanrelid if it's valid */
|
|
|
|
if (fscan->scan.scanrelid > 0)
|
|
|
|
fscan->scan.scanrelid += rtoffset;
|
|
|
|
|
|
|
|
if (fscan->fdw_scan_tlist != NIL || fscan->scan.scanrelid == 0)
|
|
|
|
{
|
|
|
|
/*
|
|
|
|
* Adjust tlist, qual, fdw_exprs, fdw_recheck_quals to reference
|
|
|
|
* foreign scan tuple
|
|
|
|
*/
|
Code review for foreign/custom join pushdown patch.
Commit e7cb7ee14555cc9c5773e2c102efd6371f6f2005 included some design
decisions that seem pretty questionable to me, and there was quite a lot
of stuff not to like about the documentation and comments. Clean up
as follows:
* Consider foreign joins only between foreign tables on the same server,
rather than between any two foreign tables with the same underlying FDW
handler function. In most if not all cases, the FDW would simply have had
to apply the same-server restriction itself (far more expensively, both for
lack of caching and because it would be repeated for each combination of
input sub-joins), or else risk nasty bugs. Anyone who's really intent on
doing something outside this restriction can always use the
set_join_pathlist_hook.
* Rename fdw_ps_tlist/custom_ps_tlist to fdw_scan_tlist/custom_scan_tlist
to better reflect what they're for, and allow these custom scan tlists
to be used even for base relations.
* Change make_foreignscan() API to include passing the fdw_scan_tlist
value, since the FDW is required to set that. Backwards compatibility
doesn't seem like an adequate reason to expect FDWs to set it in some
ad-hoc extra step, and anyway existing FDWs can just pass NIL.
* Change the API of path-generating subroutines of add_paths_to_joinrel,
and in particular that of GetForeignJoinPaths and set_join_pathlist_hook,
so that various less-used parameters are passed in a struct rather than
as separate parameter-list entries. The objective here is to reduce the
probability that future additions to those parameter lists will result in
source-level API breaks for users of these hooks. It's possible that this
is even a small win for the core code, since most CPU architectures can't
pass more than half a dozen parameters efficiently anyway. I kept root,
joinrel, outerrel, innerrel, and jointype as separate parameters to reduce
code churn in joinpath.c --- in particular, putting jointype into the
struct would have been problematic because of the subroutines' habit of
changing their local copies of that variable.
* Avoid ad-hocery in ExecAssignScanProjectionInfo. It was probably all
right for it to know about IndexOnlyScan, but if the list is to grow
we should refactor the knowledge out to the callers.
* Restore nodeForeignscan.c's previous use of the relcache to avoid
extra GetFdwRoutine lookups for base-relation scans.
* Lots of cleanup of documentation and missed comments. Re-order some
code additions into more logical places.
10 years ago
|
|
|
indexed_tlist *itlist = build_tlist_index(fscan->fdw_scan_tlist);
|
|
|
|
|
|
|
|
fscan->scan.plan.targetlist = (List *)
|
|
|
|
fix_upper_expr(root,
|
|
|
|
(Node *) fscan->scan.plan.targetlist,
|
|
|
|
itlist,
|
|
|
|
INDEX_VAR,
|
|
|
|
rtoffset);
|
|
|
|
fscan->scan.plan.qual = (List *)
|
|
|
|
fix_upper_expr(root,
|
|
|
|
(Node *) fscan->scan.plan.qual,
|
|
|
|
itlist,
|
|
|
|
INDEX_VAR,
|
|
|
|
rtoffset);
|
|
|
|
fscan->fdw_exprs = (List *)
|
|
|
|
fix_upper_expr(root,
|
|
|
|
(Node *) fscan->fdw_exprs,
|
|
|
|
itlist,
|
|
|
|
INDEX_VAR,
|
|
|
|
rtoffset);
|
|
|
|
fscan->fdw_recheck_quals = (List *)
|
|
|
|
fix_upper_expr(root,
|
|
|
|
(Node *) fscan->fdw_recheck_quals,
|
|
|
|
itlist,
|
|
|
|
INDEX_VAR,
|
|
|
|
rtoffset);
|
Code review for foreign/custom join pushdown patch.
Commit e7cb7ee14555cc9c5773e2c102efd6371f6f2005 included some design
decisions that seem pretty questionable to me, and there was quite a lot
of stuff not to like about the documentation and comments. Clean up
as follows:
* Consider foreign joins only between foreign tables on the same server,
rather than between any two foreign tables with the same underlying FDW
handler function. In most if not all cases, the FDW would simply have had
to apply the same-server restriction itself (far more expensively, both for
lack of caching and because it would be repeated for each combination of
input sub-joins), or else risk nasty bugs. Anyone who's really intent on
doing something outside this restriction can always use the
set_join_pathlist_hook.
* Rename fdw_ps_tlist/custom_ps_tlist to fdw_scan_tlist/custom_scan_tlist
to better reflect what they're for, and allow these custom scan tlists
to be used even for base relations.
* Change make_foreignscan() API to include passing the fdw_scan_tlist
value, since the FDW is required to set that. Backwards compatibility
doesn't seem like an adequate reason to expect FDWs to set it in some
ad-hoc extra step, and anyway existing FDWs can just pass NIL.
* Change the API of path-generating subroutines of add_paths_to_joinrel,
and in particular that of GetForeignJoinPaths and set_join_pathlist_hook,
so that various less-used parameters are passed in a struct rather than
as separate parameter-list entries. The objective here is to reduce the
probability that future additions to those parameter lists will result in
source-level API breaks for users of these hooks. It's possible that this
is even a small win for the core code, since most CPU architectures can't
pass more than half a dozen parameters efficiently anyway. I kept root,
joinrel, outerrel, innerrel, and jointype as separate parameters to reduce
code churn in joinpath.c --- in particular, putting jointype into the
struct would have been problematic because of the subroutines' habit of
changing their local copies of that variable.
* Avoid ad-hocery in ExecAssignScanProjectionInfo. It was probably all
right for it to know about IndexOnlyScan, but if the list is to grow
we should refactor the knowledge out to the callers.
* Restore nodeForeignscan.c's previous use of the relcache to avoid
extra GetFdwRoutine lookups for base-relation scans.
* Lots of cleanup of documentation and missed comments. Re-order some
code additions into more logical places.
10 years ago
|
|
|
pfree(itlist);
|
|
|
|
/* fdw_scan_tlist itself just needs fix_scan_list() adjustments */
|
|
|
|
fscan->fdw_scan_tlist =
|
|
|
|
fix_scan_list(root, fscan->fdw_scan_tlist, rtoffset);
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
/*
|
|
|
|
* Adjust tlist, qual, fdw_exprs, fdw_recheck_quals in the standard
|
|
|
|
* way
|
|
|
|
*/
|
Code review for foreign/custom join pushdown patch.
Commit e7cb7ee14555cc9c5773e2c102efd6371f6f2005 included some design
decisions that seem pretty questionable to me, and there was quite a lot
of stuff not to like about the documentation and comments. Clean up
as follows:
* Consider foreign joins only between foreign tables on the same server,
rather than between any two foreign tables with the same underlying FDW
handler function. In most if not all cases, the FDW would simply have had
to apply the same-server restriction itself (far more expensively, both for
lack of caching and because it would be repeated for each combination of
input sub-joins), or else risk nasty bugs. Anyone who's really intent on
doing something outside this restriction can always use the
set_join_pathlist_hook.
* Rename fdw_ps_tlist/custom_ps_tlist to fdw_scan_tlist/custom_scan_tlist
to better reflect what they're for, and allow these custom scan tlists
to be used even for base relations.
* Change make_foreignscan() API to include passing the fdw_scan_tlist
value, since the FDW is required to set that. Backwards compatibility
doesn't seem like an adequate reason to expect FDWs to set it in some
ad-hoc extra step, and anyway existing FDWs can just pass NIL.
* Change the API of path-generating subroutines of add_paths_to_joinrel,
and in particular that of GetForeignJoinPaths and set_join_pathlist_hook,
so that various less-used parameters are passed in a struct rather than
as separate parameter-list entries. The objective here is to reduce the
probability that future additions to those parameter lists will result in
source-level API breaks for users of these hooks. It's possible that this
is even a small win for the core code, since most CPU architectures can't
pass more than half a dozen parameters efficiently anyway. I kept root,
joinrel, outerrel, innerrel, and jointype as separate parameters to reduce
code churn in joinpath.c --- in particular, putting jointype into the
struct would have been problematic because of the subroutines' habit of
changing their local copies of that variable.
* Avoid ad-hocery in ExecAssignScanProjectionInfo. It was probably all
right for it to know about IndexOnlyScan, but if the list is to grow
we should refactor the knowledge out to the callers.
* Restore nodeForeignscan.c's previous use of the relcache to avoid
extra GetFdwRoutine lookups for base-relation scans.
* Lots of cleanup of documentation and missed comments. Re-order some
code additions into more logical places.
10 years ago
|
|
|
fscan->scan.plan.targetlist =
|
|
|
|
fix_scan_list(root, fscan->scan.plan.targetlist, rtoffset);
|
|
|
|
fscan->scan.plan.qual =
|
|
|
|
fix_scan_list(root, fscan->scan.plan.qual, rtoffset);
|
|
|
|
fscan->fdw_exprs =
|
|
|
|
fix_scan_list(root, fscan->fdw_exprs, rtoffset);
|
|
|
|
fscan->fdw_recheck_quals =
|
|
|
|
fix_scan_list(root, fscan->fdw_recheck_quals, rtoffset);
|
Code review for foreign/custom join pushdown patch.
Commit e7cb7ee14555cc9c5773e2c102efd6371f6f2005 included some design
decisions that seem pretty questionable to me, and there was quite a lot
of stuff not to like about the documentation and comments. Clean up
as follows:
* Consider foreign joins only between foreign tables on the same server,
rather than between any two foreign tables with the same underlying FDW
handler function. In most if not all cases, the FDW would simply have had
to apply the same-server restriction itself (far more expensively, both for
lack of caching and because it would be repeated for each combination of
input sub-joins), or else risk nasty bugs. Anyone who's really intent on
doing something outside this restriction can always use the
set_join_pathlist_hook.
* Rename fdw_ps_tlist/custom_ps_tlist to fdw_scan_tlist/custom_scan_tlist
to better reflect what they're for, and allow these custom scan tlists
to be used even for base relations.
* Change make_foreignscan() API to include passing the fdw_scan_tlist
value, since the FDW is required to set that. Backwards compatibility
doesn't seem like an adequate reason to expect FDWs to set it in some
ad-hoc extra step, and anyway existing FDWs can just pass NIL.
* Change the API of path-generating subroutines of add_paths_to_joinrel,
and in particular that of GetForeignJoinPaths and set_join_pathlist_hook,
so that various less-used parameters are passed in a struct rather than
as separate parameter-list entries. The objective here is to reduce the
probability that future additions to those parameter lists will result in
source-level API breaks for users of these hooks. It's possible that this
is even a small win for the core code, since most CPU architectures can't
pass more than half a dozen parameters efficiently anyway. I kept root,
joinrel, outerrel, innerrel, and jointype as separate parameters to reduce
code churn in joinpath.c --- in particular, putting jointype into the
struct would have been problematic because of the subroutines' habit of
changing their local copies of that variable.
* Avoid ad-hocery in ExecAssignScanProjectionInfo. It was probably all
right for it to know about IndexOnlyScan, but if the list is to grow
we should refactor the knowledge out to the callers.
* Restore nodeForeignscan.c's previous use of the relcache to avoid
extra GetFdwRoutine lookups for base-relation scans.
* Lots of cleanup of documentation and missed comments. Re-order some
code additions into more logical places.
10 years ago
|
|
|
}
|
|
|
|
|
|
|
|
/* Adjust fs_relids if needed */
|
|
|
|
if (rtoffset > 0)
|
|
|
|
{
|
|
|
|
Bitmapset *tempset = NULL;
|
|
|
|
int x = -1;
|
|
|
|
|
|
|
|
while ((x = bms_next_member(fscan->fs_relids, x)) >= 0)
|
|
|
|
tempset = bms_add_member(tempset, x + rtoffset);
|
|
|
|
fscan->fs_relids = tempset;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* set_customscan_references
|
|
|
|
* Do set_plan_references processing on a CustomScan
|
|
|
|
*/
|
|
|
|
static void
|
|
|
|
set_customscan_references(PlannerInfo *root,
|
|
|
|
CustomScan *cscan,
|
|
|
|
int rtoffset)
|
|
|
|
{
|
|
|
|
ListCell *lc;
|
|
|
|
|
Code review for foreign/custom join pushdown patch.
Commit e7cb7ee14555cc9c5773e2c102efd6371f6f2005 included some design
decisions that seem pretty questionable to me, and there was quite a lot
of stuff not to like about the documentation and comments. Clean up
as follows:
* Consider foreign joins only between foreign tables on the same server,
rather than between any two foreign tables with the same underlying FDW
handler function. In most if not all cases, the FDW would simply have had
to apply the same-server restriction itself (far more expensively, both for
lack of caching and because it would be repeated for each combination of
input sub-joins), or else risk nasty bugs. Anyone who's really intent on
doing something outside this restriction can always use the
set_join_pathlist_hook.
* Rename fdw_ps_tlist/custom_ps_tlist to fdw_scan_tlist/custom_scan_tlist
to better reflect what they're for, and allow these custom scan tlists
to be used even for base relations.
* Change make_foreignscan() API to include passing the fdw_scan_tlist
value, since the FDW is required to set that. Backwards compatibility
doesn't seem like an adequate reason to expect FDWs to set it in some
ad-hoc extra step, and anyway existing FDWs can just pass NIL.
* Change the API of path-generating subroutines of add_paths_to_joinrel,
and in particular that of GetForeignJoinPaths and set_join_pathlist_hook,
so that various less-used parameters are passed in a struct rather than
as separate parameter-list entries. The objective here is to reduce the
probability that future additions to those parameter lists will result in
source-level API breaks for users of these hooks. It's possible that this
is even a small win for the core code, since most CPU architectures can't
pass more than half a dozen parameters efficiently anyway. I kept root,
joinrel, outerrel, innerrel, and jointype as separate parameters to reduce
code churn in joinpath.c --- in particular, putting jointype into the
struct would have been problematic because of the subroutines' habit of
changing their local copies of that variable.
* Avoid ad-hocery in ExecAssignScanProjectionInfo. It was probably all
right for it to know about IndexOnlyScan, but if the list is to grow
we should refactor the knowledge out to the callers.
* Restore nodeForeignscan.c's previous use of the relcache to avoid
extra GetFdwRoutine lookups for base-relation scans.
* Lots of cleanup of documentation and missed comments. Re-order some
code additions into more logical places.
10 years ago
|
|
|
/* Adjust scanrelid if it's valid */
|
|
|
|
if (cscan->scan.scanrelid > 0)
|
|
|
|
cscan->scan.scanrelid += rtoffset;
|
|
|
|
|
|
|
|
if (cscan->custom_scan_tlist != NIL || cscan->scan.scanrelid == 0)
|
|
|
|
{
|
|
|
|
/* Adjust tlist, qual, custom_exprs to reference custom scan tuple */
|
|
|
|
indexed_tlist *itlist = build_tlist_index(cscan->custom_scan_tlist);
|
|
|
|
|
|
|
|
cscan->scan.plan.targetlist = (List *)
|
|
|
|
fix_upper_expr(root,
|
|
|
|
(Node *) cscan->scan.plan.targetlist,
|
|
|
|
itlist,
|
|
|
|
INDEX_VAR,
|
|
|
|
rtoffset);
|
|
|
|
cscan->scan.plan.qual = (List *)
|
|
|
|
fix_upper_expr(root,
|
|
|
|
(Node *) cscan->scan.plan.qual,
|
|
|
|
itlist,
|
|
|
|
INDEX_VAR,
|
|
|
|
rtoffset);
|
|
|
|
cscan->custom_exprs = (List *)
|
|
|
|
fix_upper_expr(root,
|
|
|
|
(Node *) cscan->custom_exprs,
|
|
|
|
itlist,
|
|
|
|
INDEX_VAR,
|
|
|
|
rtoffset);
|
|
|
|
pfree(itlist);
|
|
|
|
/* custom_scan_tlist itself just needs fix_scan_list() adjustments */
|
|
|
|
cscan->custom_scan_tlist =
|
|
|
|
fix_scan_list(root, cscan->custom_scan_tlist, rtoffset);
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
/* Adjust tlist, qual, custom_exprs in the standard way */
|
|
|
|
cscan->scan.plan.targetlist =
|
|
|
|
fix_scan_list(root, cscan->scan.plan.targetlist, rtoffset);
|
|
|
|
cscan->scan.plan.qual =
|
|
|
|
fix_scan_list(root, cscan->scan.plan.qual, rtoffset);
|
|
|
|
cscan->custom_exprs =
|
|
|
|
fix_scan_list(root, cscan->custom_exprs, rtoffset);
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Adjust child plan-nodes recursively, if needed */
|
|
|
|
foreach(lc, cscan->custom_plans)
|
|
|
|
{
|
|
|
|
lfirst(lc) = set_plan_refs(root, (Plan *) lfirst(lc), rtoffset);
|
|
|
|
}
|
|
|
|
|
Code review for foreign/custom join pushdown patch.
Commit e7cb7ee14555cc9c5773e2c102efd6371f6f2005 included some design
decisions that seem pretty questionable to me, and there was quite a lot
of stuff not to like about the documentation and comments. Clean up
as follows:
* Consider foreign joins only between foreign tables on the same server,
rather than between any two foreign tables with the same underlying FDW
handler function. In most if not all cases, the FDW would simply have had
to apply the same-server restriction itself (far more expensively, both for
lack of caching and because it would be repeated for each combination of
input sub-joins), or else risk nasty bugs. Anyone who's really intent on
doing something outside this restriction can always use the
set_join_pathlist_hook.
* Rename fdw_ps_tlist/custom_ps_tlist to fdw_scan_tlist/custom_scan_tlist
to better reflect what they're for, and allow these custom scan tlists
to be used even for base relations.
* Change make_foreignscan() API to include passing the fdw_scan_tlist
value, since the FDW is required to set that. Backwards compatibility
doesn't seem like an adequate reason to expect FDWs to set it in some
ad-hoc extra step, and anyway existing FDWs can just pass NIL.
* Change the API of path-generating subroutines of add_paths_to_joinrel,
and in particular that of GetForeignJoinPaths and set_join_pathlist_hook,
so that various less-used parameters are passed in a struct rather than
as separate parameter-list entries. The objective here is to reduce the
probability that future additions to those parameter lists will result in
source-level API breaks for users of these hooks. It's possible that this
is even a small win for the core code, since most CPU architectures can't
pass more than half a dozen parameters efficiently anyway. I kept root,
joinrel, outerrel, innerrel, and jointype as separate parameters to reduce
code churn in joinpath.c --- in particular, putting jointype into the
struct would have been problematic because of the subroutines' habit of
changing their local copies of that variable.
* Avoid ad-hocery in ExecAssignScanProjectionInfo. It was probably all
right for it to know about IndexOnlyScan, but if the list is to grow
we should refactor the knowledge out to the callers.
* Restore nodeForeignscan.c's previous use of the relcache to avoid
extra GetFdwRoutine lookups for base-relation scans.
* Lots of cleanup of documentation and missed comments. Re-order some
code additions into more logical places.
10 years ago
|
|
|
/* Adjust custom_relids if needed */
|
|
|
|
if (rtoffset > 0)
|
|
|
|
{
|
|
|
|
Bitmapset *tempset = NULL;
|
|
|
|
int x = -1;
|
|
|
|
|
|
|
|
while ((x = bms_next_member(cscan->custom_relids, x)) >= 0)
|
|
|
|
tempset = bms_add_member(tempset, x + rtoffset);
|
|
|
|
cscan->custom_relids = tempset;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* copyVar
|
|
|
|
* Copy a Var node.
|
|
|
|
*
|
|
|
|
* fix_scan_expr and friends do this enough times that it's worth having
|
|
|
|
* a bespoke routine instead of using the generic copyObject() function.
|
|
|
|
*/
|
|
|
|
static inline Var *
|
|
|
|
copyVar(Var *var)
|
|
|
|
{
|
|
|
|
Var *newvar = (Var *) palloc(sizeof(Var));
|
|
|
|
|
|
|
|
*newvar = *var;
|
|
|
|
return newvar;
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* fix_expr_common
|
|
|
|
* Do generic set_plan_references processing on an expression node
|
|
|
|
*
|
|
|
|
* This is code that is common to all variants of expression-fixing.
|
|
|
|
* We must look up operator opcode info for OpExpr and related nodes,
|
|
|
|
* add OIDs from regclass Const nodes into root->glob->relationOids, and
|
|
|
|
* add PlanInvalItems for user-defined functions into root->glob->invalItems.
|
Support GROUPING SETS, CUBE and ROLLUP.
This SQL standard functionality allows to aggregate data by different
GROUP BY clauses at once. Each grouping set returns rows with columns
grouped by in other sets set to NULL.
This could previously be achieved by doing each grouping as a separate
query, conjoined by UNION ALLs. Besides being considerably more concise,
grouping sets will in many cases be faster, requiring only one scan over
the underlying data.
The current implementation of grouping sets only supports using sorting
for input. Individual sets that share a sort order are computed in one
pass. If there are sets that don't share a sort order, additional sort &
aggregation steps are performed. These additional passes are sourced by
the previous sort step; thus avoiding repeated scans of the source data.
The code is structured in a way that adding support for purely using
hash aggregation or a mix of hashing and sorting is possible. Sorting
was chosen to be supported first, as it is the most generic method of
implementation.
Instead of, as in an earlier versions of the patch, representing the
chain of sort and aggregation steps as full blown planner and executor
nodes, all but the first sort are performed inside the aggregation node
itself. This avoids the need to do some unusual gymnastics to handle
having to return aggregated and non-aggregated tuples from underlying
nodes, as well as having to shut down underlying nodes early to limit
memory usage. The optimizer still builds Sort/Agg node to describe each
phase, but they're not part of the plan tree, but instead additional
data for the aggregation node. They're a convenient and preexisting way
to describe aggregation and sorting. The first (and possibly only) sort
step is still performed as a separate execution step. That retains
similarity with existing group by plans, makes rescans fairly simple,
avoids very deep plans (leading to slow explains) and easily allows to
avoid the sorting step if the underlying data is sorted by other means.
A somewhat ugly side of this patch is having to deal with a grammar
ambiguity between the new CUBE keyword and the cube extension/functions
named cube (and rollup). To avoid breaking existing deployments of the
cube extension it has not been renamed, neither has cube been made a
reserved keyword. Instead precedence hacking is used to make GROUP BY
cube(..) refer to the CUBE grouping sets feature, and not the function
cube(). To actually group by a function cube(), unlikely as that might
be, the function name has to be quoted.
Needs a catversion bump because stored rules may change.
Author: Andrew Gierth and Atri Sharma, with contributions from Andres Freund
Reviewed-By: Andres Freund, Noah Misch, Tom Lane, Svenne Krap, Tomas
Vondra, Erik Rijkers, Marti Raudsepp, Pavel Stehule
Discussion: CAOeZVidmVRe2jU6aMk_5qkxnB7dfmPROzM7Ur8JPW5j8Y5X-Lw@mail.gmail.com
10 years ago
|
|
|
* We also fill in column index lists for GROUPING() expressions.
|
|
|
|
*
|
|
|
|
* We assume it's okay to update opcode info in-place. So this could possibly
|
|
|
|
* scribble on the planner's input data structures, but it's OK.
|
|
|
|
*/
|
|
|
|
static void
|
|
|
|
fix_expr_common(PlannerInfo *root, Node *node)
|
|
|
|
{
|
|
|
|
/* We assume callers won't call us on a NULL pointer */
|
|
|
|
if (IsA(node, Aggref))
|
|
|
|
{
|
|
|
|
record_plan_function_dependency(root,
|
|
|
|
((Aggref *) node)->aggfnoid);
|
|
|
|
}
|
|
|
|
else if (IsA(node, WindowFunc))
|
|
|
|
{
|
|
|
|
record_plan_function_dependency(root,
|
|
|
|
((WindowFunc *) node)->winfnoid);
|
|
|
|
}
|
|
|
|
else if (IsA(node, FuncExpr))
|
|
|
|
{
|
|
|
|
record_plan_function_dependency(root,
|
|
|
|
((FuncExpr *) node)->funcid);
|
|
|
|
}
|
|
|
|
else if (IsA(node, OpExpr))
|
|
|
|
{
|
|
|
|
set_opfuncid((OpExpr *) node);
|
|
|
|
record_plan_function_dependency(root,
|
|
|
|
((OpExpr *) node)->opfuncid);
|
|
|
|
}
|
|
|
|
else if (IsA(node, DistinctExpr))
|
|
|
|
{
|
|
|
|
set_opfuncid((OpExpr *) node); /* rely on struct equivalence */
|
|
|
|
record_plan_function_dependency(root,
|
|
|
|
((DistinctExpr *) node)->opfuncid);
|
|
|
|
}
|
|
|
|
else if (IsA(node, NullIfExpr))
|
|
|
|
{
|
|
|
|
set_opfuncid((OpExpr *) node); /* rely on struct equivalence */
|
|
|
|
record_plan_function_dependency(root,
|
|
|
|
((NullIfExpr *) node)->opfuncid);
|
|
|
|
}
|
|
|
|
else if (IsA(node, ScalarArrayOpExpr))
|
|
|
|
{
|
|
|
|
set_sa_opfuncid((ScalarArrayOpExpr *) node);
|
|
|
|
record_plan_function_dependency(root,
|
Phase 3 of pgindent updates.
Don't move parenthesized lines to the left, even if that means they
flow past the right margin.
By default, BSD indent lines up statement continuation lines that are
within parentheses so that they start just to the right of the preceding
left parenthesis. However, traditionally, if that resulted in the
continuation line extending to the right of the desired right margin,
then indent would push it left just far enough to not overrun the margin,
if it could do so without making the continuation line start to the left of
the current statement indent. That makes for a weird mix of indentations
unless one has been completely rigid about never violating the 80-column
limit.
This behavior has been pretty universally panned by Postgres developers.
Hence, disable it with indent's new -lpl switch, so that parenthesized
lines are always lined up with the preceding left paren.
This patch is much less interesting than the first round of indent
changes, but also bulkier, so I thought it best to separate the effects.
Discussion: https://postgr.es/m/E1dAmxK-0006EE-1r@gemulon.postgresql.org
Discussion: https://postgr.es/m/30527.1495162840@sss.pgh.pa.us
8 years ago
|
|
|
((ScalarArrayOpExpr *) node)->opfuncid);
|
|
|
|
}
|
|
|
|
else if (IsA(node, Const))
|
|
|
|
{
|
|
|
|
Const *con = (Const *) node;
|
|
|
|
|
|
|
|
/* Check for regclass reference */
|
|
|
|
if (ISREGCLASSCONST(con))
|
|
|
|
root->glob->relationOids =
|
|
|
|
lappend_oid(root->glob->relationOids,
|
|
|
|
DatumGetObjectId(con->constvalue));
|
|
|
|
}
|
Support GROUPING SETS, CUBE and ROLLUP.
This SQL standard functionality allows to aggregate data by different
GROUP BY clauses at once. Each grouping set returns rows with columns
grouped by in other sets set to NULL.
This could previously be achieved by doing each grouping as a separate
query, conjoined by UNION ALLs. Besides being considerably more concise,
grouping sets will in many cases be faster, requiring only one scan over
the underlying data.
The current implementation of grouping sets only supports using sorting
for input. Individual sets that share a sort order are computed in one
pass. If there are sets that don't share a sort order, additional sort &
aggregation steps are performed. These additional passes are sourced by
the previous sort step; thus avoiding repeated scans of the source data.
The code is structured in a way that adding support for purely using
hash aggregation or a mix of hashing and sorting is possible. Sorting
was chosen to be supported first, as it is the most generic method of
implementation.
Instead of, as in an earlier versions of the patch, representing the
chain of sort and aggregation steps as full blown planner and executor
nodes, all but the first sort are performed inside the aggregation node
itself. This avoids the need to do some unusual gymnastics to handle
having to return aggregated and non-aggregated tuples from underlying
nodes, as well as having to shut down underlying nodes early to limit
memory usage. The optimizer still builds Sort/Agg node to describe each
phase, but they're not part of the plan tree, but instead additional
data for the aggregation node. They're a convenient and preexisting way
to describe aggregation and sorting. The first (and possibly only) sort
step is still performed as a separate execution step. That retains
similarity with existing group by plans, makes rescans fairly simple,
avoids very deep plans (leading to slow explains) and easily allows to
avoid the sorting step if the underlying data is sorted by other means.
A somewhat ugly side of this patch is having to deal with a grammar
ambiguity between the new CUBE keyword and the cube extension/functions
named cube (and rollup). To avoid breaking existing deployments of the
cube extension it has not been renamed, neither has cube been made a
reserved keyword. Instead precedence hacking is used to make GROUP BY
cube(..) refer to the CUBE grouping sets feature, and not the function
cube(). To actually group by a function cube(), unlikely as that might
be, the function name has to be quoted.
Needs a catversion bump because stored rules may change.
Author: Andrew Gierth and Atri Sharma, with contributions from Andres Freund
Reviewed-By: Andres Freund, Noah Misch, Tom Lane, Svenne Krap, Tomas
Vondra, Erik Rijkers, Marti Raudsepp, Pavel Stehule
Discussion: CAOeZVidmVRe2jU6aMk_5qkxnB7dfmPROzM7Ur8JPW5j8Y5X-Lw@mail.gmail.com
10 years ago
|
|
|
else if (IsA(node, GroupingFunc))
|
|
|
|
{
|
|
|
|
GroupingFunc *g = (GroupingFunc *) node;
|
|
|
|
AttrNumber *grouping_map = root->grouping_map;
|
|
|
|
|
|
|
|
/* If there are no grouping sets, we don't need this. */
|
|
|
|
|
|
|
|
Assert(grouping_map || g->cols == NIL);
|
|
|
|
|
|
|
|
if (grouping_map)
|
|
|
|
{
|
|
|
|
ListCell *lc;
|
|
|
|
List *cols = NIL;
|
|
|
|
|
|
|
|
foreach(lc, g->refs)
|
|
|
|
{
|
|
|
|
cols = lappend_int(cols, grouping_map[lfirst_int(lc)]);
|
|
|
|
}
|
|
|
|
|
|
|
|
Assert(!g->cols || equal(cols, g->cols));
|
|
|
|
|
|
|
|
if (!g->cols)
|
|
|
|
g->cols = cols;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
Implement UPDATE tab SET (col1,col2,...) = (SELECT ...), ...
This SQL-standard feature allows a sub-SELECT yielding multiple columns
(but only one row) to be used to compute the new values of several columns
to be updated. While the same results can be had with an independent
sub-SELECT per column, such a workaround can require a great deal of
duplicated computation.
The standard actually says that the source for a multi-column assignment
could be any row-valued expression. The implementation used here is
tightly tied to our existing sub-SELECT support and can't handle other
cases; the Bison grammar would have some issues with them too. However,
I don't feel too bad about this since other cases can be converted into
sub-SELECTs. For instance, "SET (a,b,c) = row_valued_function(x)" could
be written "SET (a,b,c) = (SELECT * FROM row_valued_function(x))".
11 years ago
|
|
|
/*
|
|
|
|
* fix_param_node
|
|
|
|
* Do set_plan_references processing on a Param
|
|
|
|
*
|
|
|
|
* If it's a PARAM_MULTIEXPR, replace it with the appropriate Param from
|
|
|
|
* root->multiexpr_params; otherwise no change is needed.
|
|
|
|
* Just for paranoia's sake, we make a copy of the node in either case.
|
|
|
|
*/
|
|
|
|
static Node *
|
|
|
|
fix_param_node(PlannerInfo *root, Param *p)
|
|
|
|
{
|
|
|
|
if (p->paramkind == PARAM_MULTIEXPR)
|
|
|
|
{
|
|
|
|
int subqueryid = p->paramid >> 16;
|
|
|
|
int colno = p->paramid & 0xFFFF;
|
|
|
|
List *params;
|
|
|
|
|
|
|
|
if (subqueryid <= 0 ||
|
|
|
|
subqueryid > list_length(root->multiexpr_params))
|
|
|
|
elog(ERROR, "unexpected PARAM_MULTIEXPR ID: %d", p->paramid);
|
|
|
|
params = (List *) list_nth(root->multiexpr_params, subqueryid - 1);
|
|
|
|
if (colno <= 0 || colno > list_length(params))
|
|
|
|
elog(ERROR, "unexpected PARAM_MULTIEXPR ID: %d", p->paramid);
|
|
|
|
return copyObject(list_nth(params, colno - 1));
|
|
|
|
}
|
|
|
|
return (Node *) copyObject(p);
|
Implement UPDATE tab SET (col1,col2,...) = (SELECT ...), ...
This SQL-standard feature allows a sub-SELECT yielding multiple columns
(but only one row) to be used to compute the new values of several columns
to be updated. While the same results can be had with an independent
sub-SELECT per column, such a workaround can require a great deal of
duplicated computation.
The standard actually says that the source for a multi-column assignment
could be any row-valued expression. The implementation used here is
tightly tied to our existing sub-SELECT support and can't handle other
cases; the Bison grammar would have some issues with them too. However,
I don't feel too bad about this since other cases can be converted into
sub-SELECTs. For instance, "SET (a,b,c) = row_valued_function(x)" could
be written "SET (a,b,c) = (SELECT * FROM row_valued_function(x))".
11 years ago
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* fix_scan_expr
|
|
|
|
* Do set_plan_references processing on a scan-level expression
|
|
|
|
*
|
|
|
|
* This consists of incrementing all Vars' varnos by rtoffset,
|
Implement UPDATE tab SET (col1,col2,...) = (SELECT ...), ...
This SQL-standard feature allows a sub-SELECT yielding multiple columns
(but only one row) to be used to compute the new values of several columns
to be updated. While the same results can be had with an independent
sub-SELECT per column, such a workaround can require a great deal of
duplicated computation.
The standard actually says that the source for a multi-column assignment
could be any row-valued expression. The implementation used here is
tightly tied to our existing sub-SELECT support and can't handle other
cases; the Bison grammar would have some issues with them too. However,
I don't feel too bad about this since other cases can be converted into
sub-SELECTs. For instance, "SET (a,b,c) = row_valued_function(x)" could
be written "SET (a,b,c) = (SELECT * FROM row_valued_function(x))".
11 years ago
|
|
|
* replacing PARAM_MULTIEXPR Params, expanding PlaceHolderVars,
|
Make the upper part of the planner work by generating and comparing Paths.
I've been saying we needed to do this for more than five years, and here it
finally is. This patch removes the ever-growing tangle of spaghetti logic
that grouping_planner() used to use to try to identify the best plan for
post-scan/join query steps. Now, there is (nearly) independent
consideration of each execution step, and entirely separate construction of
Paths to represent each of the possible ways to do that step. We choose
the best Path or set of Paths using the same add_path() logic that's been
used inside query_planner() for years.
In addition, this patch removes the old restriction that subquery_planner()
could return only a single Plan. It now returns a RelOptInfo containing a
set of Paths, just as query_planner() does, and the parent query level can
use each of those Paths as the basis of a SubqueryScanPath at its level.
This allows finding some optimizations that we missed before, wherein a
subquery was capable of returning presorted data and thereby avoiding a
sort in the parent level, making the overall cost cheaper even though
delivering sorted output was not the cheapest plan for the subquery in
isolation. (A couple of regression test outputs change in consequence of
that. However, there is very little change in visible planner behavior
overall, because the point of this patch is not to get immediate planning
benefits but to create the infrastructure for future improvements.)
There is a great deal left to do here. This patch unblocks a lot of
planner work that was basically impractical in the old code structure,
such as allowing FDWs to implement remote aggregation, or rewriting
plan_set_operations() to allow consideration of multiple implementation
orders for set operations. (The latter will likely require a full
rewrite of plan_set_operations(); what I've done here is only to fix it
to return Paths not Plans.) I have also left unfinished some localized
refactoring in createplan.c and planner.c, because it was not necessary
to get this patch to a working state.
Thanks to Robert Haas, David Rowley, and Amit Kapila for review.
10 years ago
|
|
|
* replacing Aggref nodes that should be replaced by initplan output Params,
|
|
|
|
* looking up operator opcode info for OpExpr and related nodes,
|
|
|
|
* and adding OIDs from regclass Const nodes into root->glob->relationOids.
|
|
|
|
*/
|
|
|
|
static Node *
|
|
|
|
fix_scan_expr(PlannerInfo *root, Node *node, int rtoffset)
|
|
|
|
{
|
|
|
|
fix_scan_expr_context context;
|
|
|
|
|
|
|
|
context.root = root;
|
|
|
|
context.rtoffset = rtoffset;
|
|
|
|
|
Implement UPDATE tab SET (col1,col2,...) = (SELECT ...), ...
This SQL-standard feature allows a sub-SELECT yielding multiple columns
(but only one row) to be used to compute the new values of several columns
to be updated. While the same results can be had with an independent
sub-SELECT per column, such a workaround can require a great deal of
duplicated computation.
The standard actually says that the source for a multi-column assignment
could be any row-valued expression. The implementation used here is
tightly tied to our existing sub-SELECT support and can't handle other
cases; the Bison grammar would have some issues with them too. However,
I don't feel too bad about this since other cases can be converted into
sub-SELECTs. For instance, "SET (a,b,c) = row_valued_function(x)" could
be written "SET (a,b,c) = (SELECT * FROM row_valued_function(x))".
11 years ago
|
|
|
if (rtoffset != 0 ||
|
|
|
|
root->multiexpr_params != NIL ||
|
Make the upper part of the planner work by generating and comparing Paths.
I've been saying we needed to do this for more than five years, and here it
finally is. This patch removes the ever-growing tangle of spaghetti logic
that grouping_planner() used to use to try to identify the best plan for
post-scan/join query steps. Now, there is (nearly) independent
consideration of each execution step, and entirely separate construction of
Paths to represent each of the possible ways to do that step. We choose
the best Path or set of Paths using the same add_path() logic that's been
used inside query_planner() for years.
In addition, this patch removes the old restriction that subquery_planner()
could return only a single Plan. It now returns a RelOptInfo containing a
set of Paths, just as query_planner() does, and the parent query level can
use each of those Paths as the basis of a SubqueryScanPath at its level.
This allows finding some optimizations that we missed before, wherein a
subquery was capable of returning presorted data and thereby avoiding a
sort in the parent level, making the overall cost cheaper even though
delivering sorted output was not the cheapest plan for the subquery in
isolation. (A couple of regression test outputs change in consequence of
that. However, there is very little change in visible planner behavior
overall, because the point of this patch is not to get immediate planning
benefits but to create the infrastructure for future improvements.)
There is a great deal left to do here. This patch unblocks a lot of
planner work that was basically impractical in the old code structure,
such as allowing FDWs to implement remote aggregation, or rewriting
plan_set_operations() to allow consideration of multiple implementation
orders for set operations. (The latter will likely require a full
rewrite of plan_set_operations(); what I've done here is only to fix it
to return Paths not Plans.) I have also left unfinished some localized
refactoring in createplan.c and planner.c, because it was not necessary
to get this patch to a working state.
Thanks to Robert Haas, David Rowley, and Amit Kapila for review.
10 years ago
|
|
|
root->glob->lastPHId != 0 ||
|
|
|
|
root->minmax_aggs != NIL)
|
|
|
|
{
|
|
|
|
return fix_scan_expr_mutator(node, &context);
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
/*
|
|
|
|
* If rtoffset == 0, we don't need to change any Vars, and if there
|
Implement UPDATE tab SET (col1,col2,...) = (SELECT ...), ...
This SQL-standard feature allows a sub-SELECT yielding multiple columns
(but only one row) to be used to compute the new values of several columns
to be updated. While the same results can be had with an independent
sub-SELECT per column, such a workaround can require a great deal of
duplicated computation.
The standard actually says that the source for a multi-column assignment
could be any row-valued expression. The implementation used here is
tightly tied to our existing sub-SELECT support and can't handle other
cases; the Bison grammar would have some issues with them too. However,
I don't feel too bad about this since other cases can be converted into
sub-SELECTs. For instance, "SET (a,b,c) = row_valued_function(x)" could
be written "SET (a,b,c) = (SELECT * FROM row_valued_function(x))".
11 years ago
|
|
|
* are no MULTIEXPR subqueries then we don't need to replace
|
|
|
|
* PARAM_MULTIEXPR Params, and if there are no placeholders anywhere
|
Make the upper part of the planner work by generating and comparing Paths.
I've been saying we needed to do this for more than five years, and here it
finally is. This patch removes the ever-growing tangle of spaghetti logic
that grouping_planner() used to use to try to identify the best plan for
post-scan/join query steps. Now, there is (nearly) independent
consideration of each execution step, and entirely separate construction of
Paths to represent each of the possible ways to do that step. We choose
the best Path or set of Paths using the same add_path() logic that's been
used inside query_planner() for years.
In addition, this patch removes the old restriction that subquery_planner()
could return only a single Plan. It now returns a RelOptInfo containing a
set of Paths, just as query_planner() does, and the parent query level can
use each of those Paths as the basis of a SubqueryScanPath at its level.
This allows finding some optimizations that we missed before, wherein a
subquery was capable of returning presorted data and thereby avoiding a
sort in the parent level, making the overall cost cheaper even though
delivering sorted output was not the cheapest plan for the subquery in
isolation. (A couple of regression test outputs change in consequence of
that. However, there is very little change in visible planner behavior
overall, because the point of this patch is not to get immediate planning
benefits but to create the infrastructure for future improvements.)
There is a great deal left to do here. This patch unblocks a lot of
planner work that was basically impractical in the old code structure,
such as allowing FDWs to implement remote aggregation, or rewriting
plan_set_operations() to allow consideration of multiple implementation
orders for set operations. (The latter will likely require a full
rewrite of plan_set_operations(); what I've done here is only to fix it
to return Paths not Plans.) I have also left unfinished some localized
refactoring in createplan.c and planner.c, because it was not necessary
to get this patch to a working state.
Thanks to Robert Haas, David Rowley, and Amit Kapila for review.
10 years ago
|
|
|
* we won't need to remove them, and if there are no minmax Aggrefs we
|
|
|
|
* won't need to replace them. Then it's OK to just scribble on the
|
Implement UPDATE tab SET (col1,col2,...) = (SELECT ...), ...
This SQL-standard feature allows a sub-SELECT yielding multiple columns
(but only one row) to be used to compute the new values of several columns
to be updated. While the same results can be had with an independent
sub-SELECT per column, such a workaround can require a great deal of
duplicated computation.
The standard actually says that the source for a multi-column assignment
could be any row-valued expression. The implementation used here is
tightly tied to our existing sub-SELECT support and can't handle other
cases; the Bison grammar would have some issues with them too. However,
I don't feel too bad about this since other cases can be converted into
sub-SELECTs. For instance, "SET (a,b,c) = row_valued_function(x)" could
be written "SET (a,b,c) = (SELECT * FROM row_valued_function(x))".
11 years ago
|
|
|
* input node tree instead of copying (since the only change, filling
|
|
|
|
* in any unset opfuncid fields, is harmless). This saves just enough
|
|
|
|
* cycles to be noticeable on trivial queries.
|
|
|
|
*/
|
|
|
|
(void) fix_scan_expr_walker(node, &context);
|
|
|
|
return node;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
static Node *
|
|
|
|
fix_scan_expr_mutator(Node *node, fix_scan_expr_context *context)
|
|
|
|
{
|
|
|
|
if (node == NULL)
|
|
|
|
return NULL;
|
|
|
|
if (IsA(node, Var))
|
|
|
|
{
|
|
|
|
Var *var = copyVar((Var *) node);
|
|
|
|
|
|
|
|
Assert(var->varlevelsup == 0);
|
|
|
|
|
|
|
|
/*
|
|
|
|
* We should not see any Vars marked INNER_VAR or OUTER_VAR. But an
|
|
|
|
* indexqual expression could contain INDEX_VAR Vars.
|
|
|
|
*/
|
|
|
|
Assert(var->varno != INNER_VAR);
|
|
|
|
Assert(var->varno != OUTER_VAR);
|
|
|
|
if (!IS_SPECIAL_VARNO(var->varno))
|
|
|
|
var->varno += context->rtoffset;
|
|
|
|
if (var->varnoold > 0)
|
|
|
|
var->varnoold += context->rtoffset;
|
|
|
|
return (Node *) var;
|
|
|
|
}
|
Implement UPDATE tab SET (col1,col2,...) = (SELECT ...), ...
This SQL-standard feature allows a sub-SELECT yielding multiple columns
(but only one row) to be used to compute the new values of several columns
to be updated. While the same results can be had with an independent
sub-SELECT per column, such a workaround can require a great deal of
duplicated computation.
The standard actually says that the source for a multi-column assignment
could be any row-valued expression. The implementation used here is
tightly tied to our existing sub-SELECT support and can't handle other
cases; the Bison grammar would have some issues with them too. However,
I don't feel too bad about this since other cases can be converted into
sub-SELECTs. For instance, "SET (a,b,c) = row_valued_function(x)" could
be written "SET (a,b,c) = (SELECT * FROM row_valued_function(x))".
11 years ago
|
|
|
if (IsA(node, Param))
|
|
|
|
return fix_param_node(context->root, (Param *) node);
|
Make the upper part of the planner work by generating and comparing Paths.
I've been saying we needed to do this for more than five years, and here it
finally is. This patch removes the ever-growing tangle of spaghetti logic
that grouping_planner() used to use to try to identify the best plan for
post-scan/join query steps. Now, there is (nearly) independent
consideration of each execution step, and entirely separate construction of
Paths to represent each of the possible ways to do that step. We choose
the best Path or set of Paths using the same add_path() logic that's been
used inside query_planner() for years.
In addition, this patch removes the old restriction that subquery_planner()
could return only a single Plan. It now returns a RelOptInfo containing a
set of Paths, just as query_planner() does, and the parent query level can
use each of those Paths as the basis of a SubqueryScanPath at its level.
This allows finding some optimizations that we missed before, wherein a
subquery was capable of returning presorted data and thereby avoiding a
sort in the parent level, making the overall cost cheaper even though
delivering sorted output was not the cheapest plan for the subquery in
isolation. (A couple of regression test outputs change in consequence of
that. However, there is very little change in visible planner behavior
overall, because the point of this patch is not to get immediate planning
benefits but to create the infrastructure for future improvements.)
There is a great deal left to do here. This patch unblocks a lot of
planner work that was basically impractical in the old code structure,
such as allowing FDWs to implement remote aggregation, or rewriting
plan_set_operations() to allow consideration of multiple implementation
orders for set operations. (The latter will likely require a full
rewrite of plan_set_operations(); what I've done here is only to fix it
to return Paths not Plans.) I have also left unfinished some localized
refactoring in createplan.c and planner.c, because it was not necessary
to get this patch to a working state.
Thanks to Robert Haas, David Rowley, and Amit Kapila for review.
10 years ago
|
|
|
if (IsA(node, Aggref))
|
|
|
|
{
|
|
|
|
Aggref *aggref = (Aggref *) node;
|
|
|
|
|
|
|
|
/* See if the Aggref should be replaced by a Param */
|
|
|
|
if (context->root->minmax_aggs != NIL &&
|
|
|
|
list_length(aggref->args) == 1)
|
|
|
|
{
|
|
|
|
TargetEntry *curTarget = (TargetEntry *) linitial(aggref->args);
|
|
|
|
ListCell *lc;
|
|
|
|
|
|
|
|
foreach(lc, context->root->minmax_aggs)
|
|
|
|
{
|
|
|
|
MinMaxAggInfo *mminfo = (MinMaxAggInfo *) lfirst(lc);
|
|
|
|
|
|
|
|
if (mminfo->aggfnoid == aggref->aggfnoid &&
|
|
|
|
equal(mminfo->target, curTarget->expr))
|
|
|
|
return (Node *) copyObject(mminfo->param);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
/* If no match, just fall through to process it normally */
|
|
|
|
}
|
|
|
|
if (IsA(node, CurrentOfExpr))
|
|
|
|
{
|
|
|
|
CurrentOfExpr *cexpr = (CurrentOfExpr *) copyObject(node);
|
|
|
|
|
|
|
|
Assert(cexpr->cvarno != INNER_VAR);
|
|
|
|
Assert(cexpr->cvarno != OUTER_VAR);
|
|
|
|
if (!IS_SPECIAL_VARNO(cexpr->cvarno))
|
|
|
|
cexpr->cvarno += context->rtoffset;
|
|
|
|
return (Node *) cexpr;
|
|
|
|
}
|
|
|
|
if (IsA(node, PlaceHolderVar))
|
|
|
|
{
|
|
|
|
/* At scan level, we should always just evaluate the contained expr */
|
|
|
|
PlaceHolderVar *phv = (PlaceHolderVar *) node;
|
|
|
|
|
|
|
|
return fix_scan_expr_mutator((Node *) phv->phexpr, context);
|
|
|
|
}
|
|
|
|
fix_expr_common(context->root, node);
|
|
|
|
return expression_tree_mutator(node, fix_scan_expr_mutator,
|
|
|
|
(void *) context);
|
|
|
|
}
|
|
|
|
|
|
|
|
static bool
|
|
|
|
fix_scan_expr_walker(Node *node, fix_scan_expr_context *context)
|
|
|
|
{
|
|
|
|
if (node == NULL)
|
|
|
|
return false;
|
|
|
|
Assert(!IsA(node, PlaceHolderVar));
|
|
|
|
fix_expr_common(context->root, node);
|
|
|
|
return expression_tree_walker(node, fix_scan_expr_walker,
|
|
|
|
(void *) context);
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* set_join_references
|
|
|
|
* Modify the target list and quals of a join node to reference its
|
|
|
|
* subplans, by setting the varnos to OUTER_VAR or INNER_VAR and setting
|
|
|
|
* attno values to the result domain number of either the corresponding
|
|
|
|
* outer or inner join tuple item. Also perform opcode lookup for these
|
|
|
|
* expressions, and add regclass OIDs to root->glob->relationOids.
|
|
|
|
*/
|
|
|
|
static void
|
|
|
|
set_join_references(PlannerInfo *root, Join *join, int rtoffset)
|
|
|
|
{
|
|
|
|
Plan *outer_plan = join->plan.lefttree;
|
|
|
|
Plan *inner_plan = join->plan.righttree;
|
|
|
|
indexed_tlist *outer_itlist;
|
|
|
|
indexed_tlist *inner_itlist;
|
|
|
|
|
|
|
|
outer_itlist = build_tlist_index(outer_plan->targetlist);
|
|
|
|
inner_itlist = build_tlist_index(inner_plan->targetlist);
|
|
|
|
|
Fix incorrect matching of subexpressions in outer-join plan nodes.
Previously we would re-use input subexpressions in all expression trees
attached to a Join plan node. However, if it's an outer join and the
subexpression appears in the nullable-side input, this is potentially
incorrect for apparently-matching subexpressions that came from above
the outer join (ie, targetlist and qpqual expressions), because the
executor will treat the subexpression value as NULL when maybe it should
not be.
The case is fairly hard to hit because (a) you need a non-strict
subexpression (else NULL is correct), and (b) we don't usually compute
expressions in the outputs of non-toplevel plan nodes. But we might do
so if the expressions are sort keys for a mergejoin, for example.
Probably in the long run we should make a more explicit distinction between
Vars appearing above and below an outer join, but that will be a major
planner redesign and not at all back-patchable. For the moment, just hack
set_join_references so that it will not match any non-Var expressions
coming from nullable inputs to expressions that came from above the join.
(This is somewhat overkill, in that a strict expression could still be
matched, but it doesn't seem worth the effort to check that.)
Per report from Qingqing Zhou. The added regression test case is based
on his example.
This has been broken for a very long time, so back-patch to all active
branches.
11 years ago
|
|
|
/*
|
|
|
|
* First process the joinquals (including merge or hash clauses). These
|
|
|
|
* are logically below the join so they can always use all values
|
|
|
|
* available from the input tlists. It's okay to also handle
|
|
|
|
* NestLoopParams now, because those couldn't refer to nullable
|
|
|
|
* subexpressions.
|
|
|
|
*/
|
|
|
|
join->joinqual = fix_join_expr(root,
|
|
|
|
join->joinqual,
|
|
|
|
outer_itlist,
|
|
|
|
inner_itlist,
|
|
|
|
(Index) 0,
|
|
|
|
rtoffset);
|
|
|
|
|
|
|
|
/* Now do join-type-specific stuff */
|
|
|
|
if (IsA(join, NestLoop))
|
|
|
|
{
|
|
|
|
NestLoop *nl = (NestLoop *) join;
|
|
|
|
ListCell *lc;
|
|
|
|
|
|
|
|
foreach(lc, nl->nestParams)
|
|
|
|
{
|
|
|
|
NestLoopParam *nlp = (NestLoopParam *) lfirst(lc);
|
|
|
|
|
|
|
|
nlp->paramval = (Var *) fix_upper_expr(root,
|
|
|
|
(Node *) nlp->paramval,
|
|
|
|
outer_itlist,
|
|
|
|
OUTER_VAR,
|
|
|
|
rtoffset);
|
|
|
|
/* Check we replaced any PlaceHolderVar with simple Var */
|
|
|
|
if (!(IsA(nlp->paramval, Var) &&
|
|
|
|
nlp->paramval->varno == OUTER_VAR))
|
|
|
|
elog(ERROR, "NestLoopParam was not reduced to a simple Var");
|
|
|
|
}
|
|
|
|
}
|
|
|
|
else if (IsA(join, MergeJoin))
|
|
|
|
{
|
|
|
|
MergeJoin *mj = (MergeJoin *) join;
|
|
|
|
|
|
|
|
mj->mergeclauses = fix_join_expr(root,
|
|
|
|
mj->mergeclauses,
|
|
|
|
outer_itlist,
|
|
|
|
inner_itlist,
|
|
|
|
(Index) 0,
|
|
|
|
rtoffset);
|
|
|
|
}
|
|
|
|
else if (IsA(join, HashJoin))
|
|
|
|
{
|
|
|
|
HashJoin *hj = (HashJoin *) join;
|
|
|
|
|
|
|
|
hj->hashclauses = fix_join_expr(root,
|
|
|
|
hj->hashclauses,
|
|
|
|
outer_itlist,
|
|
|
|
inner_itlist,
|
|
|
|
(Index) 0,
|
|
|
|
rtoffset);
|
|
|
|
}
|
|
|
|
|
Fix incorrect matching of subexpressions in outer-join plan nodes.
Previously we would re-use input subexpressions in all expression trees
attached to a Join plan node. However, if it's an outer join and the
subexpression appears in the nullable-side input, this is potentially
incorrect for apparently-matching subexpressions that came from above
the outer join (ie, targetlist and qpqual expressions), because the
executor will treat the subexpression value as NULL when maybe it should
not be.
The case is fairly hard to hit because (a) you need a non-strict
subexpression (else NULL is correct), and (b) we don't usually compute
expressions in the outputs of non-toplevel plan nodes. But we might do
so if the expressions are sort keys for a mergejoin, for example.
Probably in the long run we should make a more explicit distinction between
Vars appearing above and below an outer join, but that will be a major
planner redesign and not at all back-patchable. For the moment, just hack
set_join_references so that it will not match any non-Var expressions
coming from nullable inputs to expressions that came from above the join.
(This is somewhat overkill, in that a strict expression could still be
matched, but it doesn't seem worth the effort to check that.)
Per report from Qingqing Zhou. The added regression test case is based
on his example.
This has been broken for a very long time, so back-patch to all active
branches.
11 years ago
|
|
|
/*
|
|
|
|
* Now we need to fix up the targetlist and qpqual, which are logically
|
|
|
|
* above the join. This means they should not re-use any input expression
|
|
|
|
* that was computed in the nullable side of an outer join. Vars and
|
|
|
|
* PlaceHolderVars are fine, so we can implement this restriction just by
|
|
|
|
* clearing has_non_vars in the indexed_tlist structs.
|
|
|
|
*
|
|
|
|
* XXX This is a grotty workaround for the fact that we don't clearly
|
|
|
|
* distinguish between a Var appearing below an outer join and the "same"
|
|
|
|
* Var appearing above it. If we did, we'd not need to hack the matching
|
|
|
|
* rules this way.
|
|
|
|
*/
|
|
|
|
switch (join->jointype)
|
|
|
|
{
|
|
|
|
case JOIN_LEFT:
|
|
|
|
case JOIN_SEMI:
|
|
|
|
case JOIN_ANTI:
|
|
|
|
inner_itlist->has_non_vars = false;
|
|
|
|
break;
|
|
|
|
case JOIN_RIGHT:
|
|
|
|
outer_itlist->has_non_vars = false;
|
|
|
|
break;
|
|
|
|
case JOIN_FULL:
|
|
|
|
outer_itlist->has_non_vars = false;
|
|
|
|
inner_itlist->has_non_vars = false;
|
|
|
|
break;
|
|
|
|
default:
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
|
|
|
join->plan.targetlist = fix_join_expr(root,
|
|
|
|
join->plan.targetlist,
|
|
|
|
outer_itlist,
|
|
|
|
inner_itlist,
|
|
|
|
(Index) 0,
|
|
|
|
rtoffset);
|
|
|
|
join->plan.qual = fix_join_expr(root,
|
|
|
|
join->plan.qual,
|
|
|
|
outer_itlist,
|
|
|
|
inner_itlist,
|
|
|
|
(Index) 0,
|
|
|
|
rtoffset);
|
|
|
|
|
|
|
|
pfree(outer_itlist);
|
|
|
|
pfree(inner_itlist);
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* set_upper_references
|
|
|
|
* Update the targetlist and quals of an upper-level plan node
|
|
|
|
* to refer to the tuples returned by its lefttree subplan.
|
|
|
|
* Also perform opcode lookup for these expressions, and
|
|
|
|
* add regclass OIDs to root->glob->relationOids.
|
|
|
|
*
|
|
|
|
* This is used for single-input plan types like Agg, Group, Result.
|
|
|
|
*
|
|
|
|
* In most cases, we have to match up individual Vars in the tlist and
|
|
|
|
* qual expressions with elements of the subplan's tlist (which was
|
|
|
|
* generated by flattening these selfsame expressions, so it should have all
|
|
|
|
* the required variables). There is an important exception, however:
|
|
|
|
* depending on where we are in the plan tree, sort/group columns may have
|
|
|
|
* been pushed into the subplan tlist unflattened. If these values are also
|
|
|
|
* needed in the output then we want to reference the subplan tlist element
|
|
|
|
* rather than recomputing the expression.
|
|
|
|
*/
|
|
|
|
static void
|
|
|
|
set_upper_references(PlannerInfo *root, Plan *plan, int rtoffset)
|
|
|
|
{
|
|
|
|
Plan *subplan = plan->lefttree;
|
|
|
|
indexed_tlist *subplan_itlist;
|
|
|
|
List *output_targetlist;
|
|
|
|
ListCell *l;
|
|
|
|
|
|
|
|
subplan_itlist = build_tlist_index(subplan->targetlist);
|
|
|
|
|
|
|
|
output_targetlist = NIL;
|
|
|
|
foreach(l, plan->targetlist)
|
|
|
|
{
|
|
|
|
TargetEntry *tle = (TargetEntry *) lfirst(l);
|
|
|
|
Node *newexpr;
|
|
|
|
|
Make setrefs.c match by ressortgroupref even for plain Vars.
Previously, we skipped using search_indexed_tlist_for_sortgroupref()
if the tlist expression being sought in the child plan node was merely
a Var. This is purely an optimization, based on the theory that
search_indexed_tlist_for_var() is faster, and one copy of a Var should
be as good as another. However, the GROUPING SETS patch broke the
latter assumption: grouping columns containing the "same" Var can
sometimes have different outputs, as shown in the test case added here.
So do it the hard way whenever a ressortgroupref marking exists.
(If this seems like a bottleneck, we could imagine building a tlist index
data structure for ressortgroupref values, as we do for Vars. But I'll
let that idea go until there's some evidence it's worthwhile.)
Back-patch to 9.6. The problem also exists in 9.5 where GROUPING SETS
came in, but this patch is insufficient to resolve the problem in 9.5:
there is some obscure dependency on the upper-planner-pathification
work that happened in 9.6. Given that this is such a weird corner case,
and no end users have complained about it, it doesn't seem worth the work
to develop a fix for 9.5.
Patch by me, per a report from Heikki Linnakangas. (This does not fix
Heikki's original complaint, just the follow-on one.)
Discussion: https://postgr.es/m/aefc657e-edb2-64d5-6df1-a0828f6e9104@iki.fi
8 years ago
|
|
|
/* If it's a sort/group item, first try to match by sortref */
|
|
|
|
if (tle->ressortgroupref != 0)
|
|
|
|
{
|
|
|
|
newexpr = (Node *)
|
|
|
|
search_indexed_tlist_for_sortgroupref(tle->expr,
|
|
|
|
tle->ressortgroupref,
|
|
|
|
subplan_itlist,
|
|
|
|
OUTER_VAR);
|
|
|
|
if (!newexpr)
|
|
|
|
newexpr = fix_upper_expr(root,
|
|
|
|
(Node *) tle->expr,
|
|
|
|
subplan_itlist,
|
|
|
|
OUTER_VAR,
|
|
|
|
rtoffset);
|
|
|
|
}
|
|
|
|
else
|
|
|
|
newexpr = fix_upper_expr(root,
|
|
|
|
(Node *) tle->expr,
|
|
|
|
subplan_itlist,
|
|
|
|
OUTER_VAR,
|
|
|
|
rtoffset);
|
|
|
|
tle = flatCopyTargetEntry(tle);
|
|
|
|
tle->expr = (Expr *) newexpr;
|
|
|
|
output_targetlist = lappend(output_targetlist, tle);
|
|
|
|
}
|
|
|
|
plan->targetlist = output_targetlist;
|
|
|
|
|
|
|
|
plan->qual = (List *)
|
|
|
|
fix_upper_expr(root,
|
|
|
|
(Node *) plan->qual,
|
|
|
|
subplan_itlist,
|
|
|
|
OUTER_VAR,
|
|
|
|
rtoffset);
|
|
|
|
|
|
|
|
pfree(subplan_itlist);
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* set_param_references
|
|
|
|
* Initialize the initParam list in Gather or Gather merge node such that
|
|
|
|
* it contains reference of all the params that needs to be evaluated
|
|
|
|
* before execution of the node. It contains the initplan params that are
|
|
|
|
* being passed to the plan nodes below it.
|
|
|
|
*/
|
|
|
|
static void
|
|
|
|
set_param_references(PlannerInfo *root, Plan *plan)
|
|
|
|
{
|
|
|
|
Assert(IsA(plan, Gather) ||IsA(plan, GatherMerge));
|
|
|
|
|
|
|
|
if (plan->lefttree->extParam)
|
|
|
|
{
|
|
|
|
PlannerInfo *proot;
|
|
|
|
Bitmapset *initSetParam = NULL;
|
|
|
|
ListCell *l;
|
|
|
|
|
|
|
|
for (proot = root; proot != NULL; proot = proot->parent_root)
|
|
|
|
{
|
|
|
|
foreach(l, proot->init_plans)
|
|
|
|
{
|
|
|
|
SubPlan *initsubplan = (SubPlan *) lfirst(l);
|
|
|
|
ListCell *l2;
|
|
|
|
|
|
|
|
foreach(l2, initsubplan->setParam)
|
|
|
|
{
|
|
|
|
initSetParam = bms_add_member(initSetParam, lfirst_int(l2));
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Remember the list of all external initplan params that are used by
|
|
|
|
* the children of Gather or Gather merge node.
|
|
|
|
*/
|
|
|
|
if (IsA(plan, Gather))
|
|
|
|
((Gather *) plan)->initParam =
|
|
|
|
bms_intersect(plan->lefttree->extParam, initSetParam);
|
|
|
|
else
|
|
|
|
((GatherMerge *) plan)->initParam =
|
|
|
|
bms_intersect(plan->lefttree->extParam, initSetParam);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Recursively scan an expression tree and convert Aggrefs to the proper
|
|
|
|
* intermediate form for combining aggregates. This means (1) replacing each
|
|
|
|
* one's argument list with a single argument that is the original Aggref
|
|
|
|
* modified to show partial aggregation and (2) changing the upper Aggref to
|
|
|
|
* show combining aggregation.
|
|
|
|
*
|
|
|
|
* After this step, set_upper_references will replace the partial Aggrefs
|
|
|
|
* with Vars referencing the lower Agg plan node's outputs, so that the final
|
|
|
|
* form seen by the executor is a combining Aggref with a Var as input.
|
|
|
|
*
|
|
|
|
* It's rather messy to postpone this step until setrefs.c; ideally it'd be
|
|
|
|
* done in createplan.c. The difficulty is that once we modify the Aggref
|
|
|
|
* expressions, they will no longer be equal() to their original form and
|
|
|
|
* so cross-plan-node-level matches will fail. So this has to happen after
|
|
|
|
* the plan node above the Agg has resolved its subplan references.
|
|
|
|
*/
|
|
|
|
static Node *
|
|
|
|
convert_combining_aggrefs(Node *node, void *context)
|
|
|
|
{
|
|
|
|
if (node == NULL)
|
|
|
|
return NULL;
|
|
|
|
if (IsA(node, Aggref))
|
|
|
|
{
|
|
|
|
Aggref *orig_agg = (Aggref *) node;
|
|
|
|
Aggref *child_agg;
|
|
|
|
Aggref *parent_agg;
|
|
|
|
|
|
|
|
/* Assert we've not chosen to partial-ize any unsupported cases */
|
|
|
|
Assert(orig_agg->aggorder == NIL);
|
|
|
|
Assert(orig_agg->aggdistinct == NIL);
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Since aggregate calls can't be nested, we needn't recurse into the
|
|
|
|
* arguments. But for safety, flat-copy the Aggref node itself rather
|
|
|
|
* than modifying it in-place.
|
|
|
|
*/
|
|
|
|
child_agg = makeNode(Aggref);
|
|
|
|
memcpy(child_agg, orig_agg, sizeof(Aggref));
|
|
|
|
|
|
|
|
/*
|
|
|
|
* For the parent Aggref, we want to copy all the fields of the
|
|
|
|
* original aggregate *except* the args list, which we'll replace
|
|
|
|
* below, and the aggfilter expression, which should be applied only
|
|
|
|
* by the child not the parent. Rather than explicitly knowing about
|
|
|
|
* all the other fields here, we can momentarily modify child_agg to
|
|
|
|
* provide a suitable source for copyObject.
|
|
|
|
*/
|
|
|
|
child_agg->args = NIL;
|
|
|
|
child_agg->aggfilter = NULL;
|
|
|
|
parent_agg = copyObject(child_agg);
|
|
|
|
child_agg->args = orig_agg->args;
|
|
|
|
child_agg->aggfilter = orig_agg->aggfilter;
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Now, set up child_agg to represent the first phase of partial
|
|
|
|
* aggregation. For now, assume serialization is required.
|
|
|
|
*/
|
|
|
|
mark_partial_aggref(child_agg, AGGSPLIT_INITIAL_SERIAL);
|
|
|
|
|
|
|
|
/*
|
|
|
|
* And set up parent_agg to represent the second phase.
|
|
|
|
*/
|
|
|
|
parent_agg->args = list_make1(makeTargetEntry((Expr *) child_agg,
|
|
|
|
1, NULL, false));
|
|
|
|
mark_partial_aggref(parent_agg, AGGSPLIT_FINAL_DESERIAL);
|
|
|
|
|
|
|
|
return (Node *) parent_agg;
|
|
|
|
}
|
|
|
|
return expression_tree_mutator(node, convert_combining_aggrefs,
|
|
|
|
(void *) context);
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* set_dummy_tlist_references
|
|
|
|
* Replace the targetlist of an upper-level plan node with a simple
|
|
|
|
* list of OUTER_VAR references to its child.
|
|
|
|
*
|
|
|
|
* This is used for plan types like Sort and Append that don't evaluate
|
|
|
|
* their targetlists. Although the executor doesn't care at all what's in
|
|
|
|
* the tlist, EXPLAIN needs it to be realistic.
|
|
|
|
*
|
|
|
|
* Note: we could almost use set_upper_references() here, but it fails for
|
|
|
|
* Append for lack of a lefttree subplan. Single-purpose code is faster
|
|
|
|
* anyway.
|
|
|
|
*/
|
|
|
|
static void
|
|
|
|
set_dummy_tlist_references(Plan *plan, int rtoffset)
|
|
|
|
{
|
|
|
|
List *output_targetlist;
|
|
|
|
ListCell *l;
|
|
|
|
|
|
|
|
output_targetlist = NIL;
|
|
|
|
foreach(l, plan->targetlist)
|
|
|
|
{
|
|
|
|
TargetEntry *tle = (TargetEntry *) lfirst(l);
|
|
|
|
Var *oldvar = (Var *) tle->expr;
|
|
|
|
Var *newvar;
|
|
|
|
|
|
|
|
/*
|
|
|
|
* As in search_indexed_tlist_for_non_var(), we prefer to keep Consts
|
|
|
|
* as Consts, not Vars referencing Consts. Here, there's no speed
|
|
|
|
* advantage to be had, but it makes EXPLAIN output look cleaner, and
|
|
|
|
* again it avoids confusing the executor.
|
|
|
|
*/
|
|
|
|
if (IsA(oldvar, Const))
|
|
|
|
{
|
|
|
|
/* just reuse the existing TLE node */
|
|
|
|
output_targetlist = lappend(output_targetlist, tle);
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
|
|
|
|
newvar = makeVar(OUTER_VAR,
|
|
|
|
tle->resno,
|
|
|
|
exprType((Node *) oldvar),
|
|
|
|
exprTypmod((Node *) oldvar),
|
|
|
|
exprCollation((Node *) oldvar),
|
|
|
|
0);
|
|
|
|
if (IsA(oldvar, Var))
|
|
|
|
{
|
|
|
|
newvar->varnoold = oldvar->varno + rtoffset;
|
|
|
|
newvar->varoattno = oldvar->varattno;
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
Phase 2 of pgindent updates.
Change pg_bsd_indent to follow upstream rules for placement of comments
to the right of code, and remove pgindent hack that caused comments
following #endif to not obey the general rule.
Commit e3860ffa4dd0dad0dd9eea4be9cc1412373a8c89 wasn't actually using
the published version of pg_bsd_indent, but a hacked-up version that
tried to minimize the amount of movement of comments to the right of
code. The situation of interest is where such a comment has to be
moved to the right of its default placement at column 33 because there's
code there. BSD indent has always moved right in units of tab stops
in such cases --- but in the previous incarnation, indent was working
in 8-space tab stops, while now it knows we use 4-space tabs. So the
net result is that in about half the cases, such comments are placed
one tab stop left of before. This is better all around: it leaves
more room on the line for comment text, and it means that in such
cases the comment uniformly starts at the next 4-space tab stop after
the code, rather than sometimes one and sometimes two tabs after.
Also, ensure that comments following #endif are indented the same
as comments following other preprocessor commands such as #else.
That inconsistency turns out to have been self-inflicted damage
from a poorly-thought-through post-indent "fixup" in pgindent.
This patch is much less interesting than the first round of indent
changes, but also bulkier, so I thought it best to separate the effects.
Discussion: https://postgr.es/m/E1dAmxK-0006EE-1r@gemulon.postgresql.org
Discussion: https://postgr.es/m/30527.1495162840@sss.pgh.pa.us
8 years ago
|
|
|
newvar->varnoold = 0; /* wasn't ever a plain Var */
|
|
|
|
newvar->varoattno = 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
tle = flatCopyTargetEntry(tle);
|
|
|
|
tle->expr = (Expr *) newvar;
|
|
|
|
output_targetlist = lappend(output_targetlist, tle);
|
|
|
|
}
|
|
|
|
plan->targetlist = output_targetlist;
|
|
|
|
|
|
|
|
/* We don't touch plan->qual here */
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/*
|
|
|
|
* build_tlist_index --- build an index data structure for a child tlist
|
|
|
|
*
|
|
|
|
* In most cases, subplan tlists will be "flat" tlists with only Vars,
|
|
|
|
* so we try to optimize that case by extracting information about Vars
|
|
|
|
* in advance. Matching a parent tlist to a child is still an O(N^2)
|
|
|
|
* operation, but at least with a much smaller constant factor than plain
|
|
|
|
* tlist_member() searches.
|
|
|
|
*
|
|
|
|
* The result of this function is an indexed_tlist struct to pass to
|
|
|
|
* search_indexed_tlist_for_var() or search_indexed_tlist_for_non_var().
|
|
|
|
* When done, the indexed_tlist may be freed with a single pfree().
|
|
|
|
*/
|
|
|
|
static indexed_tlist *
|
|
|
|
build_tlist_index(List *tlist)
|
|
|
|
{
|
|
|
|
indexed_tlist *itlist;
|
|
|
|
tlist_vinfo *vinfo;
|
|
|
|
ListCell *l;
|
|
|
|
|
|
|
|
/* Create data structure with enough slots for all tlist entries */
|
|
|
|
itlist = (indexed_tlist *)
|
|
|
|
palloc(offsetof(indexed_tlist, vars) +
|
|
|
|
list_length(tlist) * sizeof(tlist_vinfo));
|
|
|
|
|
|
|
|
itlist->tlist = tlist;
|
|
|
|
itlist->has_ph_vars = false;
|
|
|
|
itlist->has_non_vars = false;
|
|
|
|
|
|
|
|
/* Find the Vars and fill in the index array */
|
|
|
|
vinfo = itlist->vars;
|
|
|
|
foreach(l, tlist)
|
|
|
|
{
|
|
|
|
TargetEntry *tle = (TargetEntry *) lfirst(l);
|
|
|
|
|
|
|
|
if (tle->expr && IsA(tle->expr, Var))
|
|
|
|
{
|
|
|
|
Var *var = (Var *) tle->expr;
|
|
|
|
|
|
|
|
vinfo->varno = var->varno;
|
|
|
|
vinfo->varattno = var->varattno;
|
|
|
|
vinfo->resno = tle->resno;
|
|
|
|
vinfo++;
|
|
|
|
}
|
|
|
|
else if (tle->expr && IsA(tle->expr, PlaceHolderVar))
|
|
|
|
itlist->has_ph_vars = true;
|
|
|
|
else
|
|
|
|
itlist->has_non_vars = true;
|
|
|
|
}
|
|
|
|
|
|
|
|
itlist->num_vars = (vinfo - itlist->vars);
|
|
|
|
|
|
|
|
return itlist;
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* build_tlist_index_other_vars --- build a restricted tlist index
|
|
|
|
*
|
|
|
|
* This is like build_tlist_index, but we only index tlist entries that
|
|
|
|
* are Vars belonging to some rel other than the one specified. We will set
|
|
|
|
* has_ph_vars (allowing PlaceHolderVars to be matched), but not has_non_vars
|
Disable support for partitionwise joins in problematic cases.
Commit f49842d, which added support for partitionwise joins, built the
child's tlist by applying adjust_appendrel_attrs() to the parent's. So in
the case where the parent's included a whole-row Var for the parent, the
child's contained a ConvertRowtypeExpr. To cope with that, that commit
added code to the planner, such as setrefs.c, but some code paths still
assumed that the tlist for a scan (or join) rel would only include Vars
and PlaceHolderVars, which was true before that commit, causing errors:
* When creating an explicit sort node for an input path for a mergejoin
path for a child join, prepare_sort_from_pathkeys() threw the 'could not
find pathkey item to sort' error.
* When deparsing a relation participating in a pushed down child join as a
subquery in contrib/postgres_fdw, get_relation_column_alias_ids() threw
the 'unexpected expression in subquery output' error.
* When performing set_plan_references() on a local join plan generated by
contrib/postgres_fdw for EvalPlanQual support for a pushed down child
join, fix_join_expr() threw the 'variable not found in subplan target
lists' error.
To fix these, two approaches have been proposed: one by Ashutosh Bapat and
one by me. While the former keeps building the child's tlist with a
ConvertRowtypeExpr, the latter builds it with a whole-row Var for the
child not to violate the planner assumption, and tries to fix it up later,
But both approaches need more work, so refuse to generate partitionwise
join paths when whole-row Vars are involved, instead. We don't need to
handle ConvertRowtypeExprs in the child's tlists for now, so this commit
also removes the changes to the planner.
Previously, partitionwise join computed attr_needed data for each child
separately, and built the child join's tlist using that data, which also
required an extra step for adding PlaceHolderVars to that tlist, but it
would be more efficient to build it from the parent join's tlist through
the adjust_appendrel_attrs() transformation. So this commit builds that
list that way, and simplifies build_joinrel_tlist() and placeholder.c as
well as part of set_append_rel_size() to basically what they were before
partitionwise join went in.
Back-patch to PG11 where partitionwise join was introduced.
Report by Rajkumar Raghuwanshi. Analysis by Ashutosh Bapat, who also
provided some of regression tests. Patch by me, reviewed by Robert Haas.
Discussion: https://postgr.es/m/CAKcux6ktu-8tefLWtQuuZBYFaZA83vUzuRd7c1YHC-yEWyYFpg@mail.gmail.com
7 years ago
|
|
|
* (so nothing other than Vars and PlaceHolderVars can be matched).
|
|
|
|
*/
|
|
|
|
static indexed_tlist *
|
|
|
|
build_tlist_index_other_vars(List *tlist, Index ignore_rel)
|
|
|
|
{
|
|
|
|
indexed_tlist *itlist;
|
|
|
|
tlist_vinfo *vinfo;
|
|
|
|
ListCell *l;
|
|
|
|
|
|
|
|
/* Create data structure with enough slots for all tlist entries */
|
|
|
|
itlist = (indexed_tlist *)
|
|
|
|
palloc(offsetof(indexed_tlist, vars) +
|
|
|
|
list_length(tlist) * sizeof(tlist_vinfo));
|
|
|
|
|
|
|
|
itlist->tlist = tlist;
|
|
|
|
itlist->has_ph_vars = false;
|
|
|
|
itlist->has_non_vars = false;
|
|
|
|
|
|
|
|
/* Find the desired Vars and fill in the index array */
|
|
|
|
vinfo = itlist->vars;
|
|
|
|
foreach(l, tlist)
|
|
|
|
{
|
|
|
|
TargetEntry *tle = (TargetEntry *) lfirst(l);
|
|
|
|
|
|
|
|
if (tle->expr && IsA(tle->expr, Var))
|
|
|
|
{
|
|
|
|
Var *var = (Var *) tle->expr;
|
|
|
|
|
|
|
|
if (var->varno != ignore_rel)
|
|
|
|
{
|
|
|
|
vinfo->varno = var->varno;
|
|
|
|
vinfo->varattno = var->varattno;
|
|
|
|
vinfo->resno = tle->resno;
|
|
|
|
vinfo++;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
else if (tle->expr && IsA(tle->expr, PlaceHolderVar))
|
|
|
|
itlist->has_ph_vars = true;
|
|
|
|
}
|
|
|
|
|
|
|
|
itlist->num_vars = (vinfo - itlist->vars);
|
|
|
|
|
|
|
|
return itlist;
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* search_indexed_tlist_for_var --- find a Var in an indexed tlist
|
|
|
|
*
|
|
|
|
* If a match is found, return a copy of the given Var with suitably
|
|
|
|
* modified varno/varattno (to wit, newvarno and the resno of the TLE entry).
|
|
|
|
* Also ensure that varnoold is incremented by rtoffset.
|
|
|
|
* If no match, return NULL.
|
|
|
|
*/
|
|
|
|
static Var *
|
|
|
|
search_indexed_tlist_for_var(Var *var, indexed_tlist *itlist,
|
|
|
|
Index newvarno, int rtoffset)
|
|
|
|
{
|
|
|
|
Index varno = var->varno;
|
|
|
|
AttrNumber varattno = var->varattno;
|
|
|
|
tlist_vinfo *vinfo;
|
|
|
|
int i;
|
|
|
|
|
|
|
|
vinfo = itlist->vars;
|
|
|
|
i = itlist->num_vars;
|
|
|
|
while (i-- > 0)
|
|
|
|
{
|
|
|
|
if (vinfo->varno == varno && vinfo->varattno == varattno)
|
|
|
|
{
|
|
|
|
/* Found a match */
|
|
|
|
Var *newvar = copyVar(var);
|
|
|
|
|
|
|
|
newvar->varno = newvarno;
|
|
|
|
newvar->varattno = vinfo->resno;
|
|
|
|
if (newvar->varnoold > 0)
|
|
|
|
newvar->varnoold += rtoffset;
|
|
|
|
return newvar;
|
|
|
|
}
|
|
|
|
vinfo++;
|
|
|
|
}
|
|
|
|
return NULL; /* no match */
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* search_indexed_tlist_for_non_var --- find a non-Var in an indexed tlist
|
|
|
|
*
|
|
|
|
* If a match is found, return a Var constructed to reference the tlist item.
|
|
|
|
* If no match, return NULL.
|
|
|
|
*
|
|
|
|
* NOTE: it is a waste of time to call this unless itlist->has_ph_vars or
|
Fix incorrect matching of subexpressions in outer-join plan nodes.
Previously we would re-use input subexpressions in all expression trees
attached to a Join plan node. However, if it's an outer join and the
subexpression appears in the nullable-side input, this is potentially
incorrect for apparently-matching subexpressions that came from above
the outer join (ie, targetlist and qpqual expressions), because the
executor will treat the subexpression value as NULL when maybe it should
not be.
The case is fairly hard to hit because (a) you need a non-strict
subexpression (else NULL is correct), and (b) we don't usually compute
expressions in the outputs of non-toplevel plan nodes. But we might do
so if the expressions are sort keys for a mergejoin, for example.
Probably in the long run we should make a more explicit distinction between
Vars appearing above and below an outer join, but that will be a major
planner redesign and not at all back-patchable. For the moment, just hack
set_join_references so that it will not match any non-Var expressions
coming from nullable inputs to expressions that came from above the join.
(This is somewhat overkill, in that a strict expression could still be
matched, but it doesn't seem worth the effort to check that.)
Per report from Qingqing Zhou. The added regression test case is based
on his example.
This has been broken for a very long time, so back-patch to all active
branches.
11 years ago
|
|
|
* itlist->has_non_vars. Furthermore, set_join_references() relies on being
|
|
|
|
* able to prevent matching of non-Vars by clearing itlist->has_non_vars,
|
|
|
|
* so there's a correctness reason not to call it unless that's set.
|
|
|
|
*/
|
|
|
|
static Var *
|
|
|
|
search_indexed_tlist_for_non_var(Expr *node,
|
|
|
|
indexed_tlist *itlist, Index newvarno)
|
|
|
|
{
|
|
|
|
TargetEntry *tle;
|
|
|
|
|
|
|
|
/*
|
|
|
|
* If it's a simple Const, replacing it with a Var is silly, even if there
|
|
|
|
* happens to be an identical Const below; a Var is more expensive to
|
|
|
|
* execute than a Const. What's more, replacing it could confuse some
|
|
|
|
* places in the executor that expect to see simple Consts for, eg,
|
|
|
|
* dropped columns.
|
|
|
|
*/
|
|
|
|
if (IsA(node, Const))
|
|
|
|
return NULL;
|
|
|
|
|
|
|
|
tle = tlist_member(node, itlist->tlist);
|
|
|
|
if (tle)
|
|
|
|
{
|
|
|
|
/* Found a matching subplan output expression */
|
|
|
|
Var *newvar;
|
|
|
|
|
|
|
|
newvar = makeVarFromTargetEntry(newvarno, tle);
|
|
|
|
newvar->varnoold = 0; /* wasn't ever a plain Var */
|
|
|
|
newvar->varoattno = 0;
|
|
|
|
return newvar;
|
|
|
|
}
|
|
|
|
return NULL; /* no match */
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* search_indexed_tlist_for_sortgroupref --- find a sort/group expression
|
|
|
|
*
|
|
|
|
* If a match is found, return a Var constructed to reference the tlist item.
|
|
|
|
* If no match, return NULL.
|
|
|
|
*
|
|
|
|
* This is needed to ensure that we select the right subplan TLE in cases
|
|
|
|
* where there are multiple textually-equal()-but-volatile sort expressions.
|
|
|
|
* And it's also faster than search_indexed_tlist_for_non_var.
|
|
|
|
*/
|
|
|
|
static Var *
|
|
|
|
search_indexed_tlist_for_sortgroupref(Expr *node,
|
|
|
|
Index sortgroupref,
|
|
|
|
indexed_tlist *itlist,
|
|
|
|
Index newvarno)
|
|
|
|
{
|
|
|
|
ListCell *lc;
|
|
|
|
|
|
|
|
foreach(lc, itlist->tlist)
|
|
|
|
{
|
|
|
|
TargetEntry *tle = (TargetEntry *) lfirst(lc);
|
|
|
|
|
|
|
|
/* The equal() check should be redundant, but let's be paranoid */
|
|
|
|
if (tle->ressortgroupref == sortgroupref &&
|
|
|
|
equal(node, tle->expr))
|
|
|
|
{
|
|
|
|
/* Found a matching subplan output expression */
|
|
|
|
Var *newvar;
|
|
|
|
|
|
|
|
newvar = makeVarFromTargetEntry(newvarno, tle);
|
Phase 2 of pgindent updates.
Change pg_bsd_indent to follow upstream rules for placement of comments
to the right of code, and remove pgindent hack that caused comments
following #endif to not obey the general rule.
Commit e3860ffa4dd0dad0dd9eea4be9cc1412373a8c89 wasn't actually using
the published version of pg_bsd_indent, but a hacked-up version that
tried to minimize the amount of movement of comments to the right of
code. The situation of interest is where such a comment has to be
moved to the right of its default placement at column 33 because there's
code there. BSD indent has always moved right in units of tab stops
in such cases --- but in the previous incarnation, indent was working
in 8-space tab stops, while now it knows we use 4-space tabs. So the
net result is that in about half the cases, such comments are placed
one tab stop left of before. This is better all around: it leaves
more room on the line for comment text, and it means that in such
cases the comment uniformly starts at the next 4-space tab stop after
the code, rather than sometimes one and sometimes two tabs after.
Also, ensure that comments following #endif are indented the same
as comments following other preprocessor commands such as #else.
That inconsistency turns out to have been self-inflicted damage
from a poorly-thought-through post-indent "fixup" in pgindent.
This patch is much less interesting than the first round of indent
changes, but also bulkier, so I thought it best to separate the effects.
Discussion: https://postgr.es/m/E1dAmxK-0006EE-1r@gemulon.postgresql.org
Discussion: https://postgr.es/m/30527.1495162840@sss.pgh.pa.us
8 years ago
|
|
|
newvar->varnoold = 0; /* wasn't ever a plain Var */
|
|
|
|
newvar->varoattno = 0;
|
|
|
|
return newvar;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return NULL; /* no match */
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* fix_join_expr
|
|
|
|
* Create a new set of targetlist entries or join qual clauses by
|
|
|
|
* changing the varno/varattno values of variables in the clauses
|
|
|
|
* to reference target list values from the outer and inner join
|
|
|
|
* relation target lists. Also perform opcode lookup and add
|
|
|
|
* regclass OIDs to root->glob->relationOids.
|
|
|
|
*
|
Fix several bugs related to ON CONFLICT's EXCLUDED pseudo relation.
Four related issues:
1) attnos/varnos/resnos for EXCLUDED were out of sync when a column
after one dropped in the underlying relation was referenced.
2) References to whole-row variables (i.e. EXCLUDED.*) lead to errors.
3) It was possible to reference system columns in the EXCLUDED pseudo
relations, even though they would not have valid contents.
4) References to EXCLUDED were rewritten by the RLS machinery, as
EXCLUDED was treated as if it were the underlying relation.
To fix the first two issues, generate the excluded targetlist with
dropped columns in mind and add an entry for whole row
variables. Instead of unconditionally adding a wholerow entry we could
pull up the expression if needed, but doing it unconditionally seems
simpler. The wholerow entry is only really needed for ruleutils/EXPLAIN
support anyway.
The remaining two issues are addressed by changing the EXCLUDED RTE to
have relkind = composite. That fits with EXCLUDED not actually being a
real relation, and allows to treat it differently in the relevant
places. scanRTEForColumn now skips looking up system columns when the
RTE has a composite relkind; fireRIRrules() already had a corresponding
check, thereby preventing RLS expansion on EXCLUDED.
Also add tests for these issues, and improve a few comments around
excluded handling in setrefs.c.
Reported-By: Peter Geoghegan, Geoff Winkless
Author: Andres Freund, Amit Langote, Peter Geoghegan
Discussion: CAEzk6fdzJ3xYQZGbcuYM2rBd2BuDkUksmK=mY9UYYDugg_GgZg@mail.gmail.com,
CAM3SWZS+CauzbiCEcg-GdE6K6ycHE_Bz6Ksszy8AoixcMHOmsA@mail.gmail.com
Backpatch: 9.5, where ON CONFLICT was introduced
10 years ago
|
|
|
* This is used in three different scenarios:
|
|
|
|
* 1) a normal join clause, where all the Vars in the clause *must* be
|
|
|
|
* replaced by OUTER_VAR or INNER_VAR references. In this case
|
|
|
|
* acceptable_rel should be zero so that any failure to match a Var will be
|
|
|
|
* reported as an error.
|
|
|
|
* 2) RETURNING clauses, which may contain both Vars of the target relation
|
|
|
|
* and Vars of other relations. In this case we want to replace the
|
|
|
|
* other-relation Vars by OUTER_VAR references, while leaving target Vars
|
|
|
|
* alone. Thus inner_itlist = NULL and acceptable_rel = the ID of the
|
|
|
|
* target relation should be passed.
|
|
|
|
* 3) ON CONFLICT UPDATE SET/WHERE clauses. Here references to EXCLUDED are
|
|
|
|
* to be replaced with INNER_VAR references, while leaving target Vars (the
|
|
|
|
* to-be-updated relation) alone. Correspondingly inner_itlist is to be
|
|
|
|
* EXCLUDED elements, outer_itlist = NULL and acceptable_rel the target
|
|
|
|
* relation.
|
|
|
|
*
|
|
|
|
* 'clauses' is the targetlist or list of join clauses
|
Add support for INSERT ... ON CONFLICT DO NOTHING/UPDATE.
The newly added ON CONFLICT clause allows to specify an alternative to
raising a unique or exclusion constraint violation error when inserting.
ON CONFLICT refers to constraints that can either be specified using a
inference clause (by specifying the columns of a unique constraint) or
by naming a unique or exclusion constraint. DO NOTHING avoids the
constraint violation, without touching the pre-existing row. DO UPDATE
SET ... [WHERE ...] updates the pre-existing tuple, and has access to
both the tuple proposed for insertion and the existing tuple; the
optional WHERE clause can be used to prevent an update from being
executed. The UPDATE SET and WHERE clauses have access to the tuple
proposed for insertion using the "magic" EXCLUDED alias, and to the
pre-existing tuple using the table name or its alias.
This feature is often referred to as upsert.
This is implemented using a new infrastructure called "speculative
insertion". It is an optimistic variant of regular insertion that first
does a pre-check for existing tuples and then attempts an insert. If a
violating tuple was inserted concurrently, the speculatively inserted
tuple is deleted and a new attempt is made. If the pre-check finds a
matching tuple the alternative DO NOTHING or DO UPDATE action is taken.
If the insertion succeeds without detecting a conflict, the tuple is
deemed inserted.
To handle the possible ambiguity between the excluded alias and a table
named excluded, and for convenience with long relation names, INSERT
INTO now can alias its target table.
Bumps catversion as stored rules change.
Author: Peter Geoghegan, with significant contributions from Heikki
Linnakangas and Andres Freund. Testing infrastructure by Jeff Janes.
Reviewed-By: Heikki Linnakangas, Andres Freund, Robert Haas, Simon Riggs,
Dean Rasheed, Stephen Frost and many others.
11 years ago
|
|
|
* 'outer_itlist' is the indexed target list of the outer join relation,
|
|
|
|
* or NULL
|
|
|
|
* 'inner_itlist' is the indexed target list of the inner join relation,
|
|
|
|
* or NULL
|
|
|
|
* 'acceptable_rel' is either zero or the rangetable index of a relation
|
|
|
|
* whose Vars may appear in the clause without provoking an error
|
|
|
|
* 'rtoffset': how much to increment varnoold by
|
|
|
|
*
|
|
|
|
* Returns the new expression tree. The original clause structure is
|
|
|
|
* not modified.
|
|
|
|
*/
|
|
|
|
static List *
|
|
|
|
fix_join_expr(PlannerInfo *root,
|
|
|
|
List *clauses,
|
|
|
|
indexed_tlist *outer_itlist,
|
|
|
|
indexed_tlist *inner_itlist,
|
|
|
|
Index acceptable_rel,
|
|
|
|
int rtoffset)
|
|
|
|
{
|
|
|
|
fix_join_expr_context context;
|
|
|
|
|
|
|
|
context.root = root;
|
|
|
|
context.outer_itlist = outer_itlist;
|
|
|
|
context.inner_itlist = inner_itlist;
|
|
|
|
context.acceptable_rel = acceptable_rel;
|
|
|
|
context.rtoffset = rtoffset;
|
|
|
|
return (List *) fix_join_expr_mutator((Node *) clauses, &context);
|
|
|
|
}
|
|
|
|
|
|
|
|
static Node *
|
|
|
|
fix_join_expr_mutator(Node *node, fix_join_expr_context *context)
|
|
|
|
{
|
|
|
|
Var *newvar;
|
|
|
|
|
|
|
|
if (node == NULL)
|
|
|
|
return NULL;
|
|
|
|
if (IsA(node, Var))
|
|
|
|
{
|
|
|
|
Var *var = (Var *) node;
|
|
|
|
|
Fix several bugs related to ON CONFLICT's EXCLUDED pseudo relation.
Four related issues:
1) attnos/varnos/resnos for EXCLUDED were out of sync when a column
after one dropped in the underlying relation was referenced.
2) References to whole-row variables (i.e. EXCLUDED.*) lead to errors.
3) It was possible to reference system columns in the EXCLUDED pseudo
relations, even though they would not have valid contents.
4) References to EXCLUDED were rewritten by the RLS machinery, as
EXCLUDED was treated as if it were the underlying relation.
To fix the first two issues, generate the excluded targetlist with
dropped columns in mind and add an entry for whole row
variables. Instead of unconditionally adding a wholerow entry we could
pull up the expression if needed, but doing it unconditionally seems
simpler. The wholerow entry is only really needed for ruleutils/EXPLAIN
support anyway.
The remaining two issues are addressed by changing the EXCLUDED RTE to
have relkind = composite. That fits with EXCLUDED not actually being a
real relation, and allows to treat it differently in the relevant
places. scanRTEForColumn now skips looking up system columns when the
RTE has a composite relkind; fireRIRrules() already had a corresponding
check, thereby preventing RLS expansion on EXCLUDED.
Also add tests for these issues, and improve a few comments around
excluded handling in setrefs.c.
Reported-By: Peter Geoghegan, Geoff Winkless
Author: Andres Freund, Amit Langote, Peter Geoghegan
Discussion: CAEzk6fdzJ3xYQZGbcuYM2rBd2BuDkUksmK=mY9UYYDugg_GgZg@mail.gmail.com,
CAM3SWZS+CauzbiCEcg-GdE6K6ycHE_Bz6Ksszy8AoixcMHOmsA@mail.gmail.com
Backpatch: 9.5, where ON CONFLICT was introduced
10 years ago
|
|
|
/* Look for the var in the input tlists, first in the outer */
|
Add support for INSERT ... ON CONFLICT DO NOTHING/UPDATE.
The newly added ON CONFLICT clause allows to specify an alternative to
raising a unique or exclusion constraint violation error when inserting.
ON CONFLICT refers to constraints that can either be specified using a
inference clause (by specifying the columns of a unique constraint) or
by naming a unique or exclusion constraint. DO NOTHING avoids the
constraint violation, without touching the pre-existing row. DO UPDATE
SET ... [WHERE ...] updates the pre-existing tuple, and has access to
both the tuple proposed for insertion and the existing tuple; the
optional WHERE clause can be used to prevent an update from being
executed. The UPDATE SET and WHERE clauses have access to the tuple
proposed for insertion using the "magic" EXCLUDED alias, and to the
pre-existing tuple using the table name or its alias.
This feature is often referred to as upsert.
This is implemented using a new infrastructure called "speculative
insertion". It is an optimistic variant of regular insertion that first
does a pre-check for existing tuples and then attempts an insert. If a
violating tuple was inserted concurrently, the speculatively inserted
tuple is deleted and a new attempt is made. If the pre-check finds a
matching tuple the alternative DO NOTHING or DO UPDATE action is taken.
If the insertion succeeds without detecting a conflict, the tuple is
deemed inserted.
To handle the possible ambiguity between the excluded alias and a table
named excluded, and for convenience with long relation names, INSERT
INTO now can alias its target table.
Bumps catversion as stored rules change.
Author: Peter Geoghegan, with significant contributions from Heikki
Linnakangas and Andres Freund. Testing infrastructure by Jeff Janes.
Reviewed-By: Heikki Linnakangas, Andres Freund, Robert Haas, Simon Riggs,
Dean Rasheed, Stephen Frost and many others.
11 years ago
|
|
|
if (context->outer_itlist)
|
|
|
|
{
|
|
|
|
newvar = search_indexed_tlist_for_var(var,
|
|
|
|
context->outer_itlist,
|
|
|
|
OUTER_VAR,
|
|
|
|
context->rtoffset);
|
|
|
|
if (newvar)
|
|
|
|
return (Node *) newvar;
|
|
|
|
}
|
|
|
|
|
Fix several bugs related to ON CONFLICT's EXCLUDED pseudo relation.
Four related issues:
1) attnos/varnos/resnos for EXCLUDED were out of sync when a column
after one dropped in the underlying relation was referenced.
2) References to whole-row variables (i.e. EXCLUDED.*) lead to errors.
3) It was possible to reference system columns in the EXCLUDED pseudo
relations, even though they would not have valid contents.
4) References to EXCLUDED were rewritten by the RLS machinery, as
EXCLUDED was treated as if it were the underlying relation.
To fix the first two issues, generate the excluded targetlist with
dropped columns in mind and add an entry for whole row
variables. Instead of unconditionally adding a wholerow entry we could
pull up the expression if needed, but doing it unconditionally seems
simpler. The wholerow entry is only really needed for ruleutils/EXPLAIN
support anyway.
The remaining two issues are addressed by changing the EXCLUDED RTE to
have relkind = composite. That fits with EXCLUDED not actually being a
real relation, and allows to treat it differently in the relevant
places. scanRTEForColumn now skips looking up system columns when the
RTE has a composite relkind; fireRIRrules() already had a corresponding
check, thereby preventing RLS expansion on EXCLUDED.
Also add tests for these issues, and improve a few comments around
excluded handling in setrefs.c.
Reported-By: Peter Geoghegan, Geoff Winkless
Author: Andres Freund, Amit Langote, Peter Geoghegan
Discussion: CAEzk6fdzJ3xYQZGbcuYM2rBd2BuDkUksmK=mY9UYYDugg_GgZg@mail.gmail.com,
CAM3SWZS+CauzbiCEcg-GdE6K6ycHE_Bz6Ksszy8AoixcMHOmsA@mail.gmail.com
Backpatch: 9.5, where ON CONFLICT was introduced
10 years ago
|
|
|
/* then in the inner. */
|
|
|
|
if (context->inner_itlist)
|
|
|
|
{
|
|
|
|
newvar = search_indexed_tlist_for_var(var,
|
|
|
|
context->inner_itlist,
|
|
|
|
INNER_VAR,
|
|
|
|
context->rtoffset);
|
|
|
|
if (newvar)
|
|
|
|
return (Node *) newvar;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* If it's for acceptable_rel, adjust and return it */
|
|
|
|
if (var->varno == context->acceptable_rel)
|
|
|
|
{
|
|
|
|
var = copyVar(var);
|
Fix planner's handling of RETURNING lists in writable CTEs.
setrefs.c failed to do "rtoffset" adjustment of Vars in RETURNING lists,
which meant they were left with the wrong varnos when the RETURNING list
was in a subquery. That was never possible before writable CTEs, of
course, but now it's broken. The executor fails to notice any problem
because ExecEvalVar just references the ecxt_scantuple for any normal
varno; but EXPLAIN breaks when the varno is wrong, as illustrated in a
recent complaint from Bartosz Dmytrak.
Since the eventual rtoffset of the subquery is not known at the time
we are preparing its plan node, the previous scheme of executing
set_returning_clause_references() at that time cannot handle this
adjustment. Fortunately, it turns out that we don't really need to do it
that way, because all the needed information is available during normal
setrefs.c execution; we just have to dig it out of the ModifyTable node.
So, do that, and get rid of the kluge of early setrefs processing of
RETURNING lists. (This is a little bit of a cheat in the case of inherited
UPDATE/DELETE, because we are not passing a "root" struct that corresponds
exactly to what the subplan was built with. But that doesn't matter, and
anyway this is less ugly than early setrefs processing was.)
Back-patch to 9.1, where the problem became possible to hit.
14 years ago
|
|
|
var->varno += context->rtoffset;
|
|
|
|
if (var->varnoold > 0)
|
|
|
|
var->varnoold += context->rtoffset;
|
|
|
|
return (Node *) var;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* No referent found for Var */
|
|
|
|
elog(ERROR, "variable not found in subplan target lists");
|
|
|
|
}
|
|
|
|
if (IsA(node, PlaceHolderVar))
|
|
|
|
{
|
|
|
|
PlaceHolderVar *phv = (PlaceHolderVar *) node;
|
|
|
|
|
|
|
|
/* See if the PlaceHolderVar has bubbled up from a lower plan node */
|
Add support for INSERT ... ON CONFLICT DO NOTHING/UPDATE.
The newly added ON CONFLICT clause allows to specify an alternative to
raising a unique or exclusion constraint violation error when inserting.
ON CONFLICT refers to constraints that can either be specified using a
inference clause (by specifying the columns of a unique constraint) or
by naming a unique or exclusion constraint. DO NOTHING avoids the
constraint violation, without touching the pre-existing row. DO UPDATE
SET ... [WHERE ...] updates the pre-existing tuple, and has access to
both the tuple proposed for insertion and the existing tuple; the
optional WHERE clause can be used to prevent an update from being
executed. The UPDATE SET and WHERE clauses have access to the tuple
proposed for insertion using the "magic" EXCLUDED alias, and to the
pre-existing tuple using the table name or its alias.
This feature is often referred to as upsert.
This is implemented using a new infrastructure called "speculative
insertion". It is an optimistic variant of regular insertion that first
does a pre-check for existing tuples and then attempts an insert. If a
violating tuple was inserted concurrently, the speculatively inserted
tuple is deleted and a new attempt is made. If the pre-check finds a
matching tuple the alternative DO NOTHING or DO UPDATE action is taken.
If the insertion succeeds without detecting a conflict, the tuple is
deemed inserted.
To handle the possible ambiguity between the excluded alias and a table
named excluded, and for convenience with long relation names, INSERT
INTO now can alias its target table.
Bumps catversion as stored rules change.
Author: Peter Geoghegan, with significant contributions from Heikki
Linnakangas and Andres Freund. Testing infrastructure by Jeff Janes.
Reviewed-By: Heikki Linnakangas, Andres Freund, Robert Haas, Simon Riggs,
Dean Rasheed, Stephen Frost and many others.
11 years ago
|
|
|
if (context->outer_itlist && context->outer_itlist->has_ph_vars)
|
|
|
|
{
|
|
|
|
newvar = search_indexed_tlist_for_non_var((Expr *) phv,
|
|
|
|
context->outer_itlist,
|
|
|
|
OUTER_VAR);
|
|
|
|
if (newvar)
|
|
|
|
return (Node *) newvar;
|
|
|
|
}
|
|
|
|
if (context->inner_itlist && context->inner_itlist->has_ph_vars)
|
|
|
|
{
|
|
|
|
newvar = search_indexed_tlist_for_non_var((Expr *) phv,
|
|
|
|
context->inner_itlist,
|
|
|
|
INNER_VAR);
|
|
|
|
if (newvar)
|
|
|
|
return (Node *) newvar;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* If not supplied by input plans, evaluate the contained expr */
|
|
|
|
return fix_join_expr_mutator((Node *) phv->phexpr, context);
|
|
|
|
}
|
Implement UPDATE tab SET (col1,col2,...) = (SELECT ...), ...
This SQL-standard feature allows a sub-SELECT yielding multiple columns
(but only one row) to be used to compute the new values of several columns
to be updated. While the same results can be had with an independent
sub-SELECT per column, such a workaround can require a great deal of
duplicated computation.
The standard actually says that the source for a multi-column assignment
could be any row-valued expression. The implementation used here is
tightly tied to our existing sub-SELECT support and can't handle other
cases; the Bison grammar would have some issues with them too. However,
I don't feel too bad about this since other cases can be converted into
sub-SELECTs. For instance, "SET (a,b,c) = row_valued_function(x)" could
be written "SET (a,b,c) = (SELECT * FROM row_valued_function(x))".
11 years ago
|
|
|
if (IsA(node, Param))
|
|
|
|
return fix_param_node(context->root, (Param *) node);
|
|
|
|
/* Try matching more complex expressions too, if tlists have any */
|
Disable support for partitionwise joins in problematic cases.
Commit f49842d, which added support for partitionwise joins, built the
child's tlist by applying adjust_appendrel_attrs() to the parent's. So in
the case where the parent's included a whole-row Var for the parent, the
child's contained a ConvertRowtypeExpr. To cope with that, that commit
added code to the planner, such as setrefs.c, but some code paths still
assumed that the tlist for a scan (or join) rel would only include Vars
and PlaceHolderVars, which was true before that commit, causing errors:
* When creating an explicit sort node for an input path for a mergejoin
path for a child join, prepare_sort_from_pathkeys() threw the 'could not
find pathkey item to sort' error.
* When deparsing a relation participating in a pushed down child join as a
subquery in contrib/postgres_fdw, get_relation_column_alias_ids() threw
the 'unexpected expression in subquery output' error.
* When performing set_plan_references() on a local join plan generated by
contrib/postgres_fdw for EvalPlanQual support for a pushed down child
join, fix_join_expr() threw the 'variable not found in subplan target
lists' error.
To fix these, two approaches have been proposed: one by Ashutosh Bapat and
one by me. While the former keeps building the child's tlist with a
ConvertRowtypeExpr, the latter builds it with a whole-row Var for the
child not to violate the planner assumption, and tries to fix it up later,
But both approaches need more work, so refuse to generate partitionwise
join paths when whole-row Vars are involved, instead. We don't need to
handle ConvertRowtypeExprs in the child's tlists for now, so this commit
also removes the changes to the planner.
Previously, partitionwise join computed attr_needed data for each child
separately, and built the child join's tlist using that data, which also
required an extra step for adding PlaceHolderVars to that tlist, but it
would be more efficient to build it from the parent join's tlist through
the adjust_appendrel_attrs() transformation. So this commit builds that
list that way, and simplifies build_joinrel_tlist() and placeholder.c as
well as part of set_append_rel_size() to basically what they were before
partitionwise join went in.
Back-patch to PG11 where partitionwise join was introduced.
Report by Rajkumar Raghuwanshi. Analysis by Ashutosh Bapat, who also
provided some of regression tests. Patch by me, reviewed by Robert Haas.
Discussion: https://postgr.es/m/CAKcux6ktu-8tefLWtQuuZBYFaZA83vUzuRd7c1YHC-yEWyYFpg@mail.gmail.com
7 years ago
|
|
|
if (context->outer_itlist && context->outer_itlist->has_non_vars)
|
|
|
|
{
|
|
|
|
newvar = search_indexed_tlist_for_non_var((Expr *) node,
|
|
|
|
context->outer_itlist,
|
|
|
|
OUTER_VAR);
|
|
|
|
if (newvar)
|
|
|
|
return (Node *) newvar;
|
|
|
|
}
|
Disable support for partitionwise joins in problematic cases.
Commit f49842d, which added support for partitionwise joins, built the
child's tlist by applying adjust_appendrel_attrs() to the parent's. So in
the case where the parent's included a whole-row Var for the parent, the
child's contained a ConvertRowtypeExpr. To cope with that, that commit
added code to the planner, such as setrefs.c, but some code paths still
assumed that the tlist for a scan (or join) rel would only include Vars
and PlaceHolderVars, which was true before that commit, causing errors:
* When creating an explicit sort node for an input path for a mergejoin
path for a child join, prepare_sort_from_pathkeys() threw the 'could not
find pathkey item to sort' error.
* When deparsing a relation participating in a pushed down child join as a
subquery in contrib/postgres_fdw, get_relation_column_alias_ids() threw
the 'unexpected expression in subquery output' error.
* When performing set_plan_references() on a local join plan generated by
contrib/postgres_fdw for EvalPlanQual support for a pushed down child
join, fix_join_expr() threw the 'variable not found in subplan target
lists' error.
To fix these, two approaches have been proposed: one by Ashutosh Bapat and
one by me. While the former keeps building the child's tlist with a
ConvertRowtypeExpr, the latter builds it with a whole-row Var for the
child not to violate the planner assumption, and tries to fix it up later,
But both approaches need more work, so refuse to generate partitionwise
join paths when whole-row Vars are involved, instead. We don't need to
handle ConvertRowtypeExprs in the child's tlists for now, so this commit
also removes the changes to the planner.
Previously, partitionwise join computed attr_needed data for each child
separately, and built the child join's tlist using that data, which also
required an extra step for adding PlaceHolderVars to that tlist, but it
would be more efficient to build it from the parent join's tlist through
the adjust_appendrel_attrs() transformation. So this commit builds that
list that way, and simplifies build_joinrel_tlist() and placeholder.c as
well as part of set_append_rel_size() to basically what they were before
partitionwise join went in.
Back-patch to PG11 where partitionwise join was introduced.
Report by Rajkumar Raghuwanshi. Analysis by Ashutosh Bapat, who also
provided some of regression tests. Patch by me, reviewed by Robert Haas.
Discussion: https://postgr.es/m/CAKcux6ktu-8tefLWtQuuZBYFaZA83vUzuRd7c1YHC-yEWyYFpg@mail.gmail.com
7 years ago
|
|
|
if (context->inner_itlist && context->inner_itlist->has_non_vars)
|
|
|
|
{
|
|
|
|
newvar = search_indexed_tlist_for_non_var((Expr *) node,
|
|
|
|
context->inner_itlist,
|
|
|
|
INNER_VAR);
|
|
|
|
if (newvar)
|
|
|
|
return (Node *) newvar;
|
|
|
|
}
|
|
|
|
fix_expr_common(context->root, node);
|
|
|
|
return expression_tree_mutator(node,
|
|
|
|
fix_join_expr_mutator,
|
|
|
|
(void *) context);
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* fix_upper_expr
|
|
|
|
* Modifies an expression tree so that all Var nodes reference outputs
|
Make the upper part of the planner work by generating and comparing Paths.
I've been saying we needed to do this for more than five years, and here it
finally is. This patch removes the ever-growing tangle of spaghetti logic
that grouping_planner() used to use to try to identify the best plan for
post-scan/join query steps. Now, there is (nearly) independent
consideration of each execution step, and entirely separate construction of
Paths to represent each of the possible ways to do that step. We choose
the best Path or set of Paths using the same add_path() logic that's been
used inside query_planner() for years.
In addition, this patch removes the old restriction that subquery_planner()
could return only a single Plan. It now returns a RelOptInfo containing a
set of Paths, just as query_planner() does, and the parent query level can
use each of those Paths as the basis of a SubqueryScanPath at its level.
This allows finding some optimizations that we missed before, wherein a
subquery was capable of returning presorted data and thereby avoiding a
sort in the parent level, making the overall cost cheaper even though
delivering sorted output was not the cheapest plan for the subquery in
isolation. (A couple of regression test outputs change in consequence of
that. However, there is very little change in visible planner behavior
overall, because the point of this patch is not to get immediate planning
benefits but to create the infrastructure for future improvements.)
There is a great deal left to do here. This patch unblocks a lot of
planner work that was basically impractical in the old code structure,
such as allowing FDWs to implement remote aggregation, or rewriting
plan_set_operations() to allow consideration of multiple implementation
orders for set operations. (The latter will likely require a full
rewrite of plan_set_operations(); what I've done here is only to fix it
to return Paths not Plans.) I have also left unfinished some localized
refactoring in createplan.c and planner.c, because it was not necessary
to get this patch to a working state.
Thanks to Robert Haas, David Rowley, and Amit Kapila for review.
10 years ago
|
|
|
* of a subplan. Also looks for Aggref nodes that should be replaced
|
|
|
|
* by initplan output Params. Also performs opcode lookup, and adds
|
|
|
|
* regclass OIDs to root->glob->relationOids.
|
|
|
|
*
|
|
|
|
* This is used to fix up target and qual expressions of non-join upper-level
|
|
|
|
* plan nodes, as well as index-only scan nodes.
|
|
|
|
*
|
|
|
|
* An error is raised if no matching var can be found in the subplan tlist
|
|
|
|
* --- so this routine should only be applied to nodes whose subplans'
|
|
|
|
* targetlists were generated by flattening the expressions used in the
|
|
|
|
* parent node.
|
|
|
|
*
|
|
|
|
* If itlist->has_non_vars is true, then we try to match whole subexpressions
|
|
|
|
* against elements of the subplan tlist, so that we can avoid recomputing
|
|
|
|
* expressions that were already computed by the subplan. (This is relatively
|
|
|
|
* expensive, so we don't want to try it in the common case where the
|
|
|
|
* subplan tlist is just a flattened list of Vars.)
|
|
|
|
*
|
|
|
|
* 'node': the tree to be fixed (a target item or qual)
|
|
|
|
* 'subplan_itlist': indexed target list for subplan (or index)
|
|
|
|
* 'newvarno': varno to use for Vars referencing tlist elements
|
|
|
|
* 'rtoffset': how much to increment varnoold by
|
|
|
|
*
|
|
|
|
* The resulting tree is a copy of the original in which all Var nodes have
|
|
|
|
* varno = newvarno, varattno = resno of corresponding targetlist element.
|
|
|
|
* The original tree is not modified.
|
|
|
|
*/
|
|
|
|
static Node *
|
|
|
|
fix_upper_expr(PlannerInfo *root,
|
|
|
|
Node *node,
|
|
|
|
indexed_tlist *subplan_itlist,
|
|
|
|
Index newvarno,
|
|
|
|
int rtoffset)
|
|
|
|
{
|
|
|
|
fix_upper_expr_context context;
|
|
|
|
|
|
|
|
context.root = root;
|
|
|
|
context.subplan_itlist = subplan_itlist;
|
|
|
|
context.newvarno = newvarno;
|
|
|
|
context.rtoffset = rtoffset;
|
|
|
|
return fix_upper_expr_mutator(node, &context);
|
|
|
|
}
|
|
|
|
|
|
|
|
static Node *
|
|
|
|
fix_upper_expr_mutator(Node *node, fix_upper_expr_context *context)
|
|
|
|
{
|
|
|
|
Var *newvar;
|
|
|
|
|
|
|
|
if (node == NULL)
|
|
|
|
return NULL;
|
|
|
|
if (IsA(node, Var))
|
|
|
|
{
|
|
|
|
Var *var = (Var *) node;
|
|
|
|
|
|
|
|
newvar = search_indexed_tlist_for_var(var,
|
|
|
|
context->subplan_itlist,
|
|
|
|
context->newvarno,
|
|
|
|
context->rtoffset);
|
|
|
|
if (!newvar)
|
|
|
|
elog(ERROR, "variable not found in subplan target list");
|
|
|
|
return (Node *) newvar;
|
|
|
|
}
|
|
|
|
if (IsA(node, PlaceHolderVar))
|
|
|
|
{
|
|
|
|
PlaceHolderVar *phv = (PlaceHolderVar *) node;
|
|
|
|
|
|
|
|
/* See if the PlaceHolderVar has bubbled up from a lower plan node */
|
|
|
|
if (context->subplan_itlist->has_ph_vars)
|
|
|
|
{
|
|
|
|
newvar = search_indexed_tlist_for_non_var((Expr *) phv,
|
|
|
|
context->subplan_itlist,
|
|
|
|
context->newvarno);
|
|
|
|
if (newvar)
|
|
|
|
return (Node *) newvar;
|
|
|
|
}
|
|
|
|
/* If not supplied by input plan, evaluate the contained expr */
|
|
|
|
return fix_upper_expr_mutator((Node *) phv->phexpr, context);
|
|
|
|
}
|
Implement UPDATE tab SET (col1,col2,...) = (SELECT ...), ...
This SQL-standard feature allows a sub-SELECT yielding multiple columns
(but only one row) to be used to compute the new values of several columns
to be updated. While the same results can be had with an independent
sub-SELECT per column, such a workaround can require a great deal of
duplicated computation.
The standard actually says that the source for a multi-column assignment
could be any row-valued expression. The implementation used here is
tightly tied to our existing sub-SELECT support and can't handle other
cases; the Bison grammar would have some issues with them too. However,
I don't feel too bad about this since other cases can be converted into
sub-SELECTs. For instance, "SET (a,b,c) = row_valued_function(x)" could
be written "SET (a,b,c) = (SELECT * FROM row_valued_function(x))".
11 years ago
|
|
|
if (IsA(node, Param))
|
|
|
|
return fix_param_node(context->root, (Param *) node);
|
Make the upper part of the planner work by generating and comparing Paths.
I've been saying we needed to do this for more than five years, and here it
finally is. This patch removes the ever-growing tangle of spaghetti logic
that grouping_planner() used to use to try to identify the best plan for
post-scan/join query steps. Now, there is (nearly) independent
consideration of each execution step, and entirely separate construction of
Paths to represent each of the possible ways to do that step. We choose
the best Path or set of Paths using the same add_path() logic that's been
used inside query_planner() for years.
In addition, this patch removes the old restriction that subquery_planner()
could return only a single Plan. It now returns a RelOptInfo containing a
set of Paths, just as query_planner() does, and the parent query level can
use each of those Paths as the basis of a SubqueryScanPath at its level.
This allows finding some optimizations that we missed before, wherein a
subquery was capable of returning presorted data and thereby avoiding a
sort in the parent level, making the overall cost cheaper even though
delivering sorted output was not the cheapest plan for the subquery in
isolation. (A couple of regression test outputs change in consequence of
that. However, there is very little change in visible planner behavior
overall, because the point of this patch is not to get immediate planning
benefits but to create the infrastructure for future improvements.)
There is a great deal left to do here. This patch unblocks a lot of
planner work that was basically impractical in the old code structure,
such as allowing FDWs to implement remote aggregation, or rewriting
plan_set_operations() to allow consideration of multiple implementation
orders for set operations. (The latter will likely require a full
rewrite of plan_set_operations(); what I've done here is only to fix it
to return Paths not Plans.) I have also left unfinished some localized
refactoring in createplan.c and planner.c, because it was not necessary
to get this patch to a working state.
Thanks to Robert Haas, David Rowley, and Amit Kapila for review.
10 years ago
|
|
|
if (IsA(node, Aggref))
|
|
|
|
{
|
|
|
|
Aggref *aggref = (Aggref *) node;
|
|
|
|
|
|
|
|
/* See if the Aggref should be replaced by a Param */
|
|
|
|
if (context->root->minmax_aggs != NIL &&
|
|
|
|
list_length(aggref->args) == 1)
|
|
|
|
{
|
|
|
|
TargetEntry *curTarget = (TargetEntry *) linitial(aggref->args);
|
|
|
|
ListCell *lc;
|
|
|
|
|
|
|
|
foreach(lc, context->root->minmax_aggs)
|
|
|
|
{
|
|
|
|
MinMaxAggInfo *mminfo = (MinMaxAggInfo *) lfirst(lc);
|
|
|
|
|
|
|
|
if (mminfo->aggfnoid == aggref->aggfnoid &&
|
|
|
|
equal(mminfo->target, curTarget->expr))
|
|
|
|
return (Node *) copyObject(mminfo->param);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
/* If no match, just fall through to process it normally */
|
|
|
|
}
|
|
|
|
/* Try matching more complex expressions too, if tlist has any */
|
Disable support for partitionwise joins in problematic cases.
Commit f49842d, which added support for partitionwise joins, built the
child's tlist by applying adjust_appendrel_attrs() to the parent's. So in
the case where the parent's included a whole-row Var for the parent, the
child's contained a ConvertRowtypeExpr. To cope with that, that commit
added code to the planner, such as setrefs.c, but some code paths still
assumed that the tlist for a scan (or join) rel would only include Vars
and PlaceHolderVars, which was true before that commit, causing errors:
* When creating an explicit sort node for an input path for a mergejoin
path for a child join, prepare_sort_from_pathkeys() threw the 'could not
find pathkey item to sort' error.
* When deparsing a relation participating in a pushed down child join as a
subquery in contrib/postgres_fdw, get_relation_column_alias_ids() threw
the 'unexpected expression in subquery output' error.
* When performing set_plan_references() on a local join plan generated by
contrib/postgres_fdw for EvalPlanQual support for a pushed down child
join, fix_join_expr() threw the 'variable not found in subplan target
lists' error.
To fix these, two approaches have been proposed: one by Ashutosh Bapat and
one by me. While the former keeps building the child's tlist with a
ConvertRowtypeExpr, the latter builds it with a whole-row Var for the
child not to violate the planner assumption, and tries to fix it up later,
But both approaches need more work, so refuse to generate partitionwise
join paths when whole-row Vars are involved, instead. We don't need to
handle ConvertRowtypeExprs in the child's tlists for now, so this commit
also removes the changes to the planner.
Previously, partitionwise join computed attr_needed data for each child
separately, and built the child join's tlist using that data, which also
required an extra step for adding PlaceHolderVars to that tlist, but it
would be more efficient to build it from the parent join's tlist through
the adjust_appendrel_attrs() transformation. So this commit builds that
list that way, and simplifies build_joinrel_tlist() and placeholder.c as
well as part of set_append_rel_size() to basically what they were before
partitionwise join went in.
Back-patch to PG11 where partitionwise join was introduced.
Report by Rajkumar Raghuwanshi. Analysis by Ashutosh Bapat, who also
provided some of regression tests. Patch by me, reviewed by Robert Haas.
Discussion: https://postgr.es/m/CAKcux6ktu-8tefLWtQuuZBYFaZA83vUzuRd7c1YHC-yEWyYFpg@mail.gmail.com
7 years ago
|
|
|
if (context->subplan_itlist->has_non_vars)
|
|
|
|
{
|
|
|
|
newvar = search_indexed_tlist_for_non_var((Expr *) node,
|
|
|
|
context->subplan_itlist,
|
|
|
|
context->newvarno);
|
|
|
|
if (newvar)
|
|
|
|
return (Node *) newvar;
|
|
|
|
}
|
|
|
|
fix_expr_common(context->root, node);
|
|
|
|
return expression_tree_mutator(node,
|
|
|
|
fix_upper_expr_mutator,
|
|
|
|
(void *) context);
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* set_returning_clause_references
|
|
|
|
* Perform setrefs.c's work on a RETURNING targetlist
|
|
|
|
*
|
|
|
|
* If the query involves more than just the result table, we have to
|
|
|
|
* adjust any Vars that refer to other tables to reference junk tlist
|
|
|
|
* entries in the top subplan's targetlist. Vars referencing the result
|
|
|
|
* table should be left alone, however (the executor will evaluate them
|
|
|
|
* using the actual heap tuple, after firing triggers if any). In the
|
Fix planner's handling of RETURNING lists in writable CTEs.
setrefs.c failed to do "rtoffset" adjustment of Vars in RETURNING lists,
which meant they were left with the wrong varnos when the RETURNING list
was in a subquery. That was never possible before writable CTEs, of
course, but now it's broken. The executor fails to notice any problem
because ExecEvalVar just references the ecxt_scantuple for any normal
varno; but EXPLAIN breaks when the varno is wrong, as illustrated in a
recent complaint from Bartosz Dmytrak.
Since the eventual rtoffset of the subquery is not known at the time
we are preparing its plan node, the previous scheme of executing
set_returning_clause_references() at that time cannot handle this
adjustment. Fortunately, it turns out that we don't really need to do it
that way, because all the needed information is available during normal
setrefs.c execution; we just have to dig it out of the ModifyTable node.
So, do that, and get rid of the kluge of early setrefs processing of
RETURNING lists. (This is a little bit of a cheat in the case of inherited
UPDATE/DELETE, because we are not passing a "root" struct that corresponds
exactly to what the subplan was built with. But that doesn't matter, and
anyway this is less ugly than early setrefs processing was.)
Back-patch to 9.1, where the problem became possible to hit.
14 years ago
|
|
|
* adjusted RETURNING list, result-table Vars will have their original
|
|
|
|
* varno (plus rtoffset), but Vars for other rels will have varno OUTER_VAR.
|
|
|
|
*
|
|
|
|
* We also must perform opcode lookup and add regclass OIDs to
|
|
|
|
* root->glob->relationOids.
|
|
|
|
*
|
|
|
|
* 'rlist': the RETURNING targetlist to be fixed
|
|
|
|
* 'topplan': the top subplan node that will be just below the ModifyTable
|
Fix planner's handling of RETURNING lists in writable CTEs.
setrefs.c failed to do "rtoffset" adjustment of Vars in RETURNING lists,
which meant they were left with the wrong varnos when the RETURNING list
was in a subquery. That was never possible before writable CTEs, of
course, but now it's broken. The executor fails to notice any problem
because ExecEvalVar just references the ecxt_scantuple for any normal
varno; but EXPLAIN breaks when the varno is wrong, as illustrated in a
recent complaint from Bartosz Dmytrak.
Since the eventual rtoffset of the subquery is not known at the time
we are preparing its plan node, the previous scheme of executing
set_returning_clause_references() at that time cannot handle this
adjustment. Fortunately, it turns out that we don't really need to do it
that way, because all the needed information is available during normal
setrefs.c execution; we just have to dig it out of the ModifyTable node.
So, do that, and get rid of the kluge of early setrefs processing of
RETURNING lists. (This is a little bit of a cheat in the case of inherited
UPDATE/DELETE, because we are not passing a "root" struct that corresponds
exactly to what the subplan was built with. But that doesn't matter, and
anyway this is less ugly than early setrefs processing was.)
Back-patch to 9.1, where the problem became possible to hit.
14 years ago
|
|
|
* node (note it's not yet passed through set_plan_refs)
|
|
|
|
* 'resultRelation': RT index of the associated result relation
|
Fix planner's handling of RETURNING lists in writable CTEs.
setrefs.c failed to do "rtoffset" adjustment of Vars in RETURNING lists,
which meant they were left with the wrong varnos when the RETURNING list
was in a subquery. That was never possible before writable CTEs, of
course, but now it's broken. The executor fails to notice any problem
because ExecEvalVar just references the ecxt_scantuple for any normal
varno; but EXPLAIN breaks when the varno is wrong, as illustrated in a
recent complaint from Bartosz Dmytrak.
Since the eventual rtoffset of the subquery is not known at the time
we are preparing its plan node, the previous scheme of executing
set_returning_clause_references() at that time cannot handle this
adjustment. Fortunately, it turns out that we don't really need to do it
that way, because all the needed information is available during normal
setrefs.c execution; we just have to dig it out of the ModifyTable node.
So, do that, and get rid of the kluge of early setrefs processing of
RETURNING lists. (This is a little bit of a cheat in the case of inherited
UPDATE/DELETE, because we are not passing a "root" struct that corresponds
exactly to what the subplan was built with. But that doesn't matter, and
anyway this is less ugly than early setrefs processing was.)
Back-patch to 9.1, where the problem became possible to hit.
14 years ago
|
|
|
* 'rtoffset': how much to increment varnos by
|
|
|
|
*
|
Fix planner's handling of RETURNING lists in writable CTEs.
setrefs.c failed to do "rtoffset" adjustment of Vars in RETURNING lists,
which meant they were left with the wrong varnos when the RETURNING list
was in a subquery. That was never possible before writable CTEs, of
course, but now it's broken. The executor fails to notice any problem
because ExecEvalVar just references the ecxt_scantuple for any normal
varno; but EXPLAIN breaks when the varno is wrong, as illustrated in a
recent complaint from Bartosz Dmytrak.
Since the eventual rtoffset of the subquery is not known at the time
we are preparing its plan node, the previous scheme of executing
set_returning_clause_references() at that time cannot handle this
adjustment. Fortunately, it turns out that we don't really need to do it
that way, because all the needed information is available during normal
setrefs.c execution; we just have to dig it out of the ModifyTable node.
So, do that, and get rid of the kluge of early setrefs processing of
RETURNING lists. (This is a little bit of a cheat in the case of inherited
UPDATE/DELETE, because we are not passing a "root" struct that corresponds
exactly to what the subplan was built with. But that doesn't matter, and
anyway this is less ugly than early setrefs processing was.)
Back-patch to 9.1, where the problem became possible to hit.
14 years ago
|
|
|
* Note: the given 'root' is for the parent query level, not the 'topplan'.
|
|
|
|
* This does not matter currently since we only access the dependency-item
|
|
|
|
* lists in root->glob, but it would need some hacking if we wanted a root
|
|
|
|
* that actually matches the subplan.
|
|
|
|
*
|
|
|
|
* Note: resultRelation is not yet adjusted by rtoffset.
|
|
|
|
*/
|
Fix planner's handling of RETURNING lists in writable CTEs.
setrefs.c failed to do "rtoffset" adjustment of Vars in RETURNING lists,
which meant they were left with the wrong varnos when the RETURNING list
was in a subquery. That was never possible before writable CTEs, of
course, but now it's broken. The executor fails to notice any problem
because ExecEvalVar just references the ecxt_scantuple for any normal
varno; but EXPLAIN breaks when the varno is wrong, as illustrated in a
recent complaint from Bartosz Dmytrak.
Since the eventual rtoffset of the subquery is not known at the time
we are preparing its plan node, the previous scheme of executing
set_returning_clause_references() at that time cannot handle this
adjustment. Fortunately, it turns out that we don't really need to do it
that way, because all the needed information is available during normal
setrefs.c execution; we just have to dig it out of the ModifyTable node.
So, do that, and get rid of the kluge of early setrefs processing of
RETURNING lists. (This is a little bit of a cheat in the case of inherited
UPDATE/DELETE, because we are not passing a "root" struct that corresponds
exactly to what the subplan was built with. But that doesn't matter, and
anyway this is less ugly than early setrefs processing was.)
Back-patch to 9.1, where the problem became possible to hit.
14 years ago
|
|
|
static List *
|
|
|
|
set_returning_clause_references(PlannerInfo *root,
|
|
|
|
List *rlist,
|
|
|
|
Plan *topplan,
|
Fix planner's handling of RETURNING lists in writable CTEs.
setrefs.c failed to do "rtoffset" adjustment of Vars in RETURNING lists,
which meant they were left with the wrong varnos when the RETURNING list
was in a subquery. That was never possible before writable CTEs, of
course, but now it's broken. The executor fails to notice any problem
because ExecEvalVar just references the ecxt_scantuple for any normal
varno; but EXPLAIN breaks when the varno is wrong, as illustrated in a
recent complaint from Bartosz Dmytrak.
Since the eventual rtoffset of the subquery is not known at the time
we are preparing its plan node, the previous scheme of executing
set_returning_clause_references() at that time cannot handle this
adjustment. Fortunately, it turns out that we don't really need to do it
that way, because all the needed information is available during normal
setrefs.c execution; we just have to dig it out of the ModifyTable node.
So, do that, and get rid of the kluge of early setrefs processing of
RETURNING lists. (This is a little bit of a cheat in the case of inherited
UPDATE/DELETE, because we are not passing a "root" struct that corresponds
exactly to what the subplan was built with. But that doesn't matter, and
anyway this is less ugly than early setrefs processing was.)
Back-patch to 9.1, where the problem became possible to hit.
14 years ago
|
|
|
Index resultRelation,
|
|
|
|
int rtoffset)
|
|
|
|
{
|
|
|
|
indexed_tlist *itlist;
|
|
|
|
|
|
|
|
/*
|
|
|
|
* We can perform the desired Var fixup by abusing the fix_join_expr
|
|
|
|
* machinery that formerly handled inner indexscan fixup. We search the
|
|
|
|
* top plan's targetlist for Vars of non-result relations, and use
|
|
|
|
* fix_join_expr to convert RETURNING Vars into references to those tlist
|
|
|
|
* entries, while leaving result-rel Vars as-is.
|
|
|
|
*
|
|
|
|
* PlaceHolderVars will also be sought in the targetlist, but no
|
|
|
|
* more-complex expressions will be. Note that it is not possible for a
|
|
|
|
* PlaceHolderVar to refer to the result relation, since the result is
|
|
|
|
* never below an outer join. If that case could happen, we'd have to be
|
|
|
|
* prepared to pick apart the PlaceHolderVar and evaluate its contained
|
|
|
|
* expression instead.
|
|
|
|
*/
|
|
|
|
itlist = build_tlist_index_other_vars(topplan->targetlist, resultRelation);
|
|
|
|
|
|
|
|
rlist = fix_join_expr(root,
|
|
|
|
rlist,
|
|
|
|
itlist,
|
|
|
|
NULL,
|
|
|
|
resultRelation,
|
Fix planner's handling of RETURNING lists in writable CTEs.
setrefs.c failed to do "rtoffset" adjustment of Vars in RETURNING lists,
which meant they were left with the wrong varnos when the RETURNING list
was in a subquery. That was never possible before writable CTEs, of
course, but now it's broken. The executor fails to notice any problem
because ExecEvalVar just references the ecxt_scantuple for any normal
varno; but EXPLAIN breaks when the varno is wrong, as illustrated in a
recent complaint from Bartosz Dmytrak.
Since the eventual rtoffset of the subquery is not known at the time
we are preparing its plan node, the previous scheme of executing
set_returning_clause_references() at that time cannot handle this
adjustment. Fortunately, it turns out that we don't really need to do it
that way, because all the needed information is available during normal
setrefs.c execution; we just have to dig it out of the ModifyTable node.
So, do that, and get rid of the kluge of early setrefs processing of
RETURNING lists. (This is a little bit of a cheat in the case of inherited
UPDATE/DELETE, because we are not passing a "root" struct that corresponds
exactly to what the subplan was built with. But that doesn't matter, and
anyway this is less ugly than early setrefs processing was.)
Back-patch to 9.1, where the problem became possible to hit.
14 years ago
|
|
|
rtoffset);
|
|
|
|
|
|
|
|
pfree(itlist);
|
|
|
|
|
|
|
|
return rlist;
|
|
|
|
}
|
|
|
|
|
Support GROUPING SETS, CUBE and ROLLUP.
This SQL standard functionality allows to aggregate data by different
GROUP BY clauses at once. Each grouping set returns rows with columns
grouped by in other sets set to NULL.
This could previously be achieved by doing each grouping as a separate
query, conjoined by UNION ALLs. Besides being considerably more concise,
grouping sets will in many cases be faster, requiring only one scan over
the underlying data.
The current implementation of grouping sets only supports using sorting
for input. Individual sets that share a sort order are computed in one
pass. If there are sets that don't share a sort order, additional sort &
aggregation steps are performed. These additional passes are sourced by
the previous sort step; thus avoiding repeated scans of the source data.
The code is structured in a way that adding support for purely using
hash aggregation or a mix of hashing and sorting is possible. Sorting
was chosen to be supported first, as it is the most generic method of
implementation.
Instead of, as in an earlier versions of the patch, representing the
chain of sort and aggregation steps as full blown planner and executor
nodes, all but the first sort are performed inside the aggregation node
itself. This avoids the need to do some unusual gymnastics to handle
having to return aggregated and non-aggregated tuples from underlying
nodes, as well as having to shut down underlying nodes early to limit
memory usage. The optimizer still builds Sort/Agg node to describe each
phase, but they're not part of the plan tree, but instead additional
data for the aggregation node. They're a convenient and preexisting way
to describe aggregation and sorting. The first (and possibly only) sort
step is still performed as a separate execution step. That retains
similarity with existing group by plans, makes rescans fairly simple,
avoids very deep plans (leading to slow explains) and easily allows to
avoid the sorting step if the underlying data is sorted by other means.
A somewhat ugly side of this patch is having to deal with a grammar
ambiguity between the new CUBE keyword and the cube extension/functions
named cube (and rollup). To avoid breaking existing deployments of the
cube extension it has not been renamed, neither has cube been made a
reserved keyword. Instead precedence hacking is used to make GROUP BY
cube(..) refer to the CUBE grouping sets feature, and not the function
cube(). To actually group by a function cube(), unlikely as that might
be, the function name has to be quoted.
Needs a catversion bump because stored rules may change.
Author: Andrew Gierth and Atri Sharma, with contributions from Andres Freund
Reviewed-By: Andres Freund, Noah Misch, Tom Lane, Svenne Krap, Tomas
Vondra, Erik Rijkers, Marti Raudsepp, Pavel Stehule
Discussion: CAOeZVidmVRe2jU6aMk_5qkxnB7dfmPROzM7Ur8JPW5j8Y5X-Lw@mail.gmail.com
10 years ago
|
|
|
|
|
|
|
/*****************************************************************************
|
|
|
|
* QUERY DEPENDENCY MANAGEMENT
|
|
|
|
*****************************************************************************/
|
|
|
|
|
|
|
|
/*
|
|
|
|
* record_plan_function_dependency
|
|
|
|
* Mark the current plan as depending on a particular function.
|
|
|
|
*
|
|
|
|
* This is exported so that the function-inlining code can record a
|
|
|
|
* dependency on a function that it's removed from the plan tree.
|
|
|
|
*/
|
|
|
|
void
|
|
|
|
record_plan_function_dependency(PlannerInfo *root, Oid funcid)
|
|
|
|
{
|
|
|
|
/*
|
|
|
|
* For performance reasons, we don't bother to track built-in functions;
|
|
|
|
* we just assume they'll never change (or at least not in ways that'd
|
|
|
|
* invalidate plans using them). For this purpose we can consider a
|
|
|
|
* built-in function to be one with OID less than FirstBootstrapObjectId.
|
|
|
|
* Note that the OID generator guarantees never to generate such an OID
|
|
|
|
* after startup, even at OID wraparound.
|
|
|
|
*/
|
|
|
|
if (funcid >= (Oid) FirstBootstrapObjectId)
|
|
|
|
{
|
|
|
|
PlanInvalItem *inval_item = makeNode(PlanInvalItem);
|
|
|
|
|
|
|
|
/*
|
|
|
|
* It would work to use any syscache on pg_proc, but the easiest is
|
|
|
|
* PROCOID since we already have the function's OID at hand. Note
|
|
|
|
* that plancache.c knows we use PROCOID.
|
|
|
|
*/
|
|
|
|
inval_item->cacheId = PROCOID;
|
|
|
|
inval_item->hashValue = GetSysCacheHashValue1(PROCOID,
|
Phase 3 of pgindent updates.
Don't move parenthesized lines to the left, even if that means they
flow past the right margin.
By default, BSD indent lines up statement continuation lines that are
within parentheses so that they start just to the right of the preceding
left parenthesis. However, traditionally, if that resulted in the
continuation line extending to the right of the desired right margin,
then indent would push it left just far enough to not overrun the margin,
if it could do so without making the continuation line start to the left of
the current statement indent. That makes for a weird mix of indentations
unless one has been completely rigid about never violating the 80-column
limit.
This behavior has been pretty universally panned by Postgres developers.
Hence, disable it with indent's new -lpl switch, so that parenthesized
lines are always lined up with the preceding left paren.
This patch is much less interesting than the first round of indent
changes, but also bulkier, so I thought it best to separate the effects.
Discussion: https://postgr.es/m/E1dAmxK-0006EE-1r@gemulon.postgresql.org
Discussion: https://postgr.es/m/30527.1495162840@sss.pgh.pa.us
8 years ago
|
|
|
ObjectIdGetDatum(funcid));
|
|
|
|
|
|
|
|
root->glob->invalItems = lappend(root->glob->invalItems, inval_item);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* extract_query_dependencies
|
Avoid invalidating all foreign-join cached plans when user mappings change.
We must not push down a foreign join when the foreign tables involved
should be accessed under different user mappings. Previously we tried
to enforce that rule literally during planning, but that meant that the
resulting plans were dependent on the current contents of the
pg_user_mapping catalog, and we had to blow away all cached plans
containing any remote join when anything at all changed in pg_user_mapping.
This could have been improved somewhat, but the fact that a syscache inval
callback has very limited info about what changed made it hard to do better
within that design. Instead, let's change the planner to not consider user
mappings per se, but to allow a foreign join if both RTEs have the same
checkAsUser value. If they do, then they necessarily will use the same
user mapping at runtime, and we don't need to know specifically which one
that is. Post-plan-time changes in pg_user_mapping no longer require any
plan invalidation.
This rule does give up some optimization ability, to wit where two foreign
table references come from views with different owners or one's from a view
and one's directly in the query, but nonetheless the same user mapping
would have applied. We'll sacrifice the first case, but to not regress
more than we have to in the second case, allow a foreign join involving
both zero and nonzero checkAsUser values if the nonzero one is the same as
the prevailing effective userID. In that case, mark the plan as only
runnable by that userID.
The plancache code already had a notion of plans being userID-specific,
in order to support RLS. It was a little confused though, in particular
lacking clarity of thought as to whether it was the rewritten query or just
the finished plan that's dependent on the userID. Rearrange that code so
that it's clearer what depends on which, and so that the same logic applies
to both RLS-injected role dependency and foreign-join-injected role
dependency.
Note that this patch doesn't remove the other issue mentioned in the
original complaint, which is that while we'll reliably stop using a foreign
join if it's disallowed in a new context, we might fail to start using a
foreign join if it's now allowed, but we previously created a generic
cached plan that didn't use one. It was agreed that the chance of winning
that way was not high enough to justify the much larger number of plan
invalidations that would have to occur if we tried to cause it to happen.
In passing, clean up randomly-varying spelling of EXPLAIN commands in
postgres_fdw.sql, and fix a COSTS ON example that had been allowed to
leak into the committed tests.
This reverts most of commits fbe5a3fb7 and 5d4171d1c, which were the
previous attempt at ensuring we wouldn't push down foreign joins that
span permissions contexts.
Etsuro Fujita and Tom Lane
Discussion: <d49c1e5b-f059-20f4-c132-e9752ee0113e@lab.ntt.co.jp>
9 years ago
|
|
|
* Given a rewritten, but not yet planned, query or queries
|
|
|
|
* (i.e. a Query node or list of Query nodes), extract dependencies
|
|
|
|
* just as set_plan_references would do. Also detect whether any
|
|
|
|
* rewrite steps were affected by RLS.
|
|
|
|
*
|
|
|
|
* This is needed by plancache.c to handle invalidation of cached unplanned
|
|
|
|
* queries.
|
|
|
|
*/
|
|
|
|
void
|
|
|
|
extract_query_dependencies(Node *query,
|
|
|
|
List **relationOids,
|
Row-Level Security Policies (RLS)
Building on the updatable security-barrier views work, add the
ability to define policies on tables to limit the set of rows
which are returned from a query and which are allowed to be added
to a table. Expressions defined by the policy for filtering are
added to the security barrier quals of the query, while expressions
defined to check records being added to a table are added to the
with-check options of the query.
New top-level commands are CREATE/ALTER/DROP POLICY and are
controlled by the table owner. Row Security is able to be enabled
and disabled by the owner on a per-table basis using
ALTER TABLE .. ENABLE/DISABLE ROW SECURITY.
Per discussion, ROW SECURITY is disabled on tables by default and
must be enabled for policies on the table to be used. If no
policies exist on a table with ROW SECURITY enabled, a default-deny
policy is used and no records will be visible.
By default, row security is applied at all times except for the
table owner and the superuser. A new GUC, row_security, is added
which can be set to ON, OFF, or FORCE. When set to FORCE, row
security will be applied even for the table owner and superusers.
When set to OFF, row security will be disabled when allowed and an
error will be thrown if the user does not have rights to bypass row
security.
Per discussion, pg_dump sets row_security = OFF by default to ensure
that exports and backups will have all data in the table or will
error if there are insufficient privileges to bypass row security.
A new option has been added to pg_dump, --enable-row-security, to
ask pg_dump to export with row security enabled.
A new role capability, BYPASSRLS, which can only be set by the
superuser, is added to allow other users to be able to bypass row
security using row_security = OFF.
Many thanks to the various individuals who have helped with the
design, particularly Robert Haas for his feedback.
Authors include Craig Ringer, KaiGai Kohei, Adam Brightwell, Dean
Rasheed, with additional changes and rework by me.
Reviewers have included all of the above, Greg Smith,
Jeff McCormick, and Robert Haas.
11 years ago
|
|
|
List **invalItems,
|
|
|
|
bool *hasRowSecurity)
|
|
|
|
{
|
|
|
|
PlannerGlobal glob;
|
|
|
|
PlannerInfo root;
|
|
|
|
|
|
|
|
/* Make up dummy planner state so we can use this module's machinery */
|
|
|
|
MemSet(&glob, 0, sizeof(glob));
|
|
|
|
glob.type = T_PlannerGlobal;
|
|
|
|
glob.relationOids = NIL;
|
|
|
|
glob.invalItems = NIL;
|
Avoid invalidating all foreign-join cached plans when user mappings change.
We must not push down a foreign join when the foreign tables involved
should be accessed under different user mappings. Previously we tried
to enforce that rule literally during planning, but that meant that the
resulting plans were dependent on the current contents of the
pg_user_mapping catalog, and we had to blow away all cached plans
containing any remote join when anything at all changed in pg_user_mapping.
This could have been improved somewhat, but the fact that a syscache inval
callback has very limited info about what changed made it hard to do better
within that design. Instead, let's change the planner to not consider user
mappings per se, but to allow a foreign join if both RTEs have the same
checkAsUser value. If they do, then they necessarily will use the same
user mapping at runtime, and we don't need to know specifically which one
that is. Post-plan-time changes in pg_user_mapping no longer require any
plan invalidation.
This rule does give up some optimization ability, to wit where two foreign
table references come from views with different owners or one's from a view
and one's directly in the query, but nonetheless the same user mapping
would have applied. We'll sacrifice the first case, but to not regress
more than we have to in the second case, allow a foreign join involving
both zero and nonzero checkAsUser values if the nonzero one is the same as
the prevailing effective userID. In that case, mark the plan as only
runnable by that userID.
The plancache code already had a notion of plans being userID-specific,
in order to support RLS. It was a little confused though, in particular
lacking clarity of thought as to whether it was the rewritten query or just
the finished plan that's dependent on the userID. Rearrange that code so
that it's clearer what depends on which, and so that the same logic applies
to both RLS-injected role dependency and foreign-join-injected role
dependency.
Note that this patch doesn't remove the other issue mentioned in the
original complaint, which is that while we'll reliably stop using a foreign
join if it's disallowed in a new context, we might fail to start using a
foreign join if it's now allowed, but we previously created a generic
cached plan that didn't use one. It was agreed that the chance of winning
that way was not high enough to justify the much larger number of plan
invalidations that would have to occur if we tried to cause it to happen.
In passing, clean up randomly-varying spelling of EXPLAIN commands in
postgres_fdw.sql, and fix a COSTS ON example that had been allowed to
leak into the committed tests.
This reverts most of commits fbe5a3fb7 and 5d4171d1c, which were the
previous attempt at ensuring we wouldn't push down foreign joins that
span permissions contexts.
Etsuro Fujita and Tom Lane
Discussion: <d49c1e5b-f059-20f4-c132-e9752ee0113e@lab.ntt.co.jp>
9 years ago
|
|
|
/* Hack: we use glob.dependsOnRole to collect hasRowSecurity flags */
|
|
|
|
glob.dependsOnRole = false;
|
|
|
|
|
|
|
|
MemSet(&root, 0, sizeof(root));
|
|
|
|
root.type = T_PlannerInfo;
|
|
|
|
root.glob = &glob;
|
|
|
|
|
|
|
|
(void) extract_query_dependencies_walker(query, &root);
|
|
|
|
|
|
|
|
*relationOids = glob.relationOids;
|
|
|
|
*invalItems = glob.invalItems;
|
Avoid invalidating all foreign-join cached plans when user mappings change.
We must not push down a foreign join when the foreign tables involved
should be accessed under different user mappings. Previously we tried
to enforce that rule literally during planning, but that meant that the
resulting plans were dependent on the current contents of the
pg_user_mapping catalog, and we had to blow away all cached plans
containing any remote join when anything at all changed in pg_user_mapping.
This could have been improved somewhat, but the fact that a syscache inval
callback has very limited info about what changed made it hard to do better
within that design. Instead, let's change the planner to not consider user
mappings per se, but to allow a foreign join if both RTEs have the same
checkAsUser value. If they do, then they necessarily will use the same
user mapping at runtime, and we don't need to know specifically which one
that is. Post-plan-time changes in pg_user_mapping no longer require any
plan invalidation.
This rule does give up some optimization ability, to wit where two foreign
table references come from views with different owners or one's from a view
and one's directly in the query, but nonetheless the same user mapping
would have applied. We'll sacrifice the first case, but to not regress
more than we have to in the second case, allow a foreign join involving
both zero and nonzero checkAsUser values if the nonzero one is the same as
the prevailing effective userID. In that case, mark the plan as only
runnable by that userID.
The plancache code already had a notion of plans being userID-specific,
in order to support RLS. It was a little confused though, in particular
lacking clarity of thought as to whether it was the rewritten query or just
the finished plan that's dependent on the userID. Rearrange that code so
that it's clearer what depends on which, and so that the same logic applies
to both RLS-injected role dependency and foreign-join-injected role
dependency.
Note that this patch doesn't remove the other issue mentioned in the
original complaint, which is that while we'll reliably stop using a foreign
join if it's disallowed in a new context, we might fail to start using a
foreign join if it's now allowed, but we previously created a generic
cached plan that didn't use one. It was agreed that the chance of winning
that way was not high enough to justify the much larger number of plan
invalidations that would have to occur if we tried to cause it to happen.
In passing, clean up randomly-varying spelling of EXPLAIN commands in
postgres_fdw.sql, and fix a COSTS ON example that had been allowed to
leak into the committed tests.
This reverts most of commits fbe5a3fb7 and 5d4171d1c, which were the
previous attempt at ensuring we wouldn't push down foreign joins that
span permissions contexts.
Etsuro Fujita and Tom Lane
Discussion: <d49c1e5b-f059-20f4-c132-e9752ee0113e@lab.ntt.co.jp>
9 years ago
|
|
|
*hasRowSecurity = glob.dependsOnRole;
|
|
|
|
}
|
|
|
|
|
|
|
|
static bool
|
|
|
|
extract_query_dependencies_walker(Node *node, PlannerInfo *context)
|
|
|
|
{
|
|
|
|
if (node == NULL)
|
|
|
|
return false;
|
|
|
|
Assert(!IsA(node, PlaceHolderVar));
|
|
|
|
/* Extract function dependencies and check for regclass Consts */
|
|
|
|
fix_expr_common(context, node);
|
|
|
|
if (IsA(node, Query))
|
|
|
|
{
|
|
|
|
Query *query = (Query *) node;
|
|
|
|
ListCell *lc;
|
|
|
|
|
|
|
|
if (query->commandType == CMD_UTILITY)
|
|
|
|
{
|
Restructure SELECT INTO's parsetree representation into CreateTableAsStmt.
Making this operation look like a utility statement seems generally a good
idea, and particularly so in light of the desire to provide command
triggers for utility statements. The original choice of representing it as
SELECT with an IntoClause appendage had metastasized into rather a lot of
places, unfortunately, so that this patch is a great deal more complicated
than one might at first expect.
In particular, keeping EXPLAIN working for SELECT INTO and CREATE TABLE AS
subcommands required restructuring some EXPLAIN-related APIs. Add-on code
that calls ExplainOnePlan or ExplainOneUtility, or uses
ExplainOneQuery_hook, will need adjustment.
Also, the cases PREPARE ... SELECT INTO and CREATE RULE ... SELECT INTO,
which formerly were accepted though undocumented, are no longer accepted.
The PREPARE case can be replaced with use of CREATE TABLE AS EXECUTE.
The CREATE RULE case doesn't seem to have much real-world use (since the
rule would work only once before failing with "table already exists"),
so we'll not bother with that one.
Both SELECT INTO and CREATE TABLE AS still return a command tag of
"SELECT nnnn". There was some discussion of returning "CREATE TABLE nnnn",
but for the moment backwards compatibility wins the day.
Andres Freund and Tom Lane
14 years ago
|
|
|
/*
|
|
|
|
* Ignore utility statements, except those (such as EXPLAIN) that
|
|
|
|
* contain a parsed-but-not-planned query.
|
|
|
|
*/
|
|
|
|
query = UtilityContainsQuery(query->utilityStmt);
|
|
|
|
if (query == NULL)
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
Avoid invalidating all foreign-join cached plans when user mappings change.
We must not push down a foreign join when the foreign tables involved
should be accessed under different user mappings. Previously we tried
to enforce that rule literally during planning, but that meant that the
resulting plans were dependent on the current contents of the
pg_user_mapping catalog, and we had to blow away all cached plans
containing any remote join when anything at all changed in pg_user_mapping.
This could have been improved somewhat, but the fact that a syscache inval
callback has very limited info about what changed made it hard to do better
within that design. Instead, let's change the planner to not consider user
mappings per se, but to allow a foreign join if both RTEs have the same
checkAsUser value. If they do, then they necessarily will use the same
user mapping at runtime, and we don't need to know specifically which one
that is. Post-plan-time changes in pg_user_mapping no longer require any
plan invalidation.
This rule does give up some optimization ability, to wit where two foreign
table references come from views with different owners or one's from a view
and one's directly in the query, but nonetheless the same user mapping
would have applied. We'll sacrifice the first case, but to not regress
more than we have to in the second case, allow a foreign join involving
both zero and nonzero checkAsUser values if the nonzero one is the same as
the prevailing effective userID. In that case, mark the plan as only
runnable by that userID.
The plancache code already had a notion of plans being userID-specific,
in order to support RLS. It was a little confused though, in particular
lacking clarity of thought as to whether it was the rewritten query or just
the finished plan that's dependent on the userID. Rearrange that code so
that it's clearer what depends on which, and so that the same logic applies
to both RLS-injected role dependency and foreign-join-injected role
dependency.
Note that this patch doesn't remove the other issue mentioned in the
original complaint, which is that while we'll reliably stop using a foreign
join if it's disallowed in a new context, we might fail to start using a
foreign join if it's now allowed, but we previously created a generic
cached plan that didn't use one. It was agreed that the chance of winning
that way was not high enough to justify the much larger number of plan
invalidations that would have to occur if we tried to cause it to happen.
In passing, clean up randomly-varying spelling of EXPLAIN commands in
postgres_fdw.sql, and fix a COSTS ON example that had been allowed to
leak into the committed tests.
This reverts most of commits fbe5a3fb7 and 5d4171d1c, which were the
previous attempt at ensuring we wouldn't push down foreign joins that
span permissions contexts.
Etsuro Fujita and Tom Lane
Discussion: <d49c1e5b-f059-20f4-c132-e9752ee0113e@lab.ntt.co.jp>
9 years ago
|
|
|
/* Remember if any Query has RLS quals applied by rewriter */
|
|
|
|
if (query->hasRowSecurity)
|
|
|
|
context->glob->dependsOnRole = true;
|
|
|
|
|
|
|
|
/* Collect relation OIDs in this Query's rtable */
|
|
|
|
foreach(lc, query->rtable)
|
|
|
|
{
|
|
|
|
RangeTblEntry *rte = (RangeTblEntry *) lfirst(lc);
|
|
|
|
|
|
|
|
if (rte->rtekind == RTE_RELATION)
|
|
|
|
context->glob->relationOids =
|
|
|
|
lappend_oid(context->glob->relationOids, rte->relid);
|
|
|
|
else if (rte->rtekind == RTE_NAMEDTUPLESTORE &&
|
|
|
|
OidIsValid(rte->relid))
|
|
|
|
context->glob->relationOids =
|
|
|
|
lappend_oid(context->glob->relationOids,
|
|
|
|
rte->relid);
|
|
|
|
}
|
|
|
|
|
|
|
|
/* And recurse into the query's subexpressions */
|
|
|
|
return query_tree_walker(query, extract_query_dependencies_walker,
|
|
|
|
(void *) context, 0);
|
|
|
|
}
|
|
|
|
return expression_tree_walker(node, extract_query_dependencies_walker,
|
|
|
|
(void *) context);
|
|
|
|
}
|