Prevent generation of bogus subquery scan paths.
Commit 0927d2f46ddd4cf7d6bf2cc84b3be923e0aedc52 didn't check that consider_parallel was set for the target relation or account for the possibility that required_outer might be non-empty. To prevent future bugs of this ilk, add some assertions to add_partial_path and do a bit of future-proofing of the code recently added to recurse_set_operations. Report by Andreas Seltenreich. Patch by Jeevan Chalke. Review by Amit Kapila and by me. Discussion: http://postgr.es/m/CAM2+6=U+9otsyF2fYB8x_2TBeHTR90itarqW=qAEjN-kHaC7kw@mail.gmail.com
This commit is contained in:
parent
f35f30f74b
commit
dc1057fcd8
@ -2243,26 +2243,31 @@ set_subquery_pathlist(PlannerInfo *root, RelOptInfo *rel,
|
|||||||
pathkeys, required_outer));
|
pathkeys, required_outer));
|
||||||
}
|
}
|
||||||
|
|
||||||
/* If consider_parallel is false, there should be no partial paths. */
|
/* If outer rel allows parallelism, do same for partial paths. */
|
||||||
Assert(sub_final_rel->consider_parallel ||
|
if (rel->consider_parallel && bms_is_empty(required_outer))
|
||||||
sub_final_rel->partial_pathlist == NIL);
|
|
||||||
|
|
||||||
/* Same for partial paths. */
|
|
||||||
foreach(lc, sub_final_rel->partial_pathlist)
|
|
||||||
{
|
{
|
||||||
Path *subpath = (Path *) lfirst(lc);
|
/* If consider_parallel is false, there should be no partial paths. */
|
||||||
List *pathkeys;
|
Assert(sub_final_rel->consider_parallel ||
|
||||||
|
sub_final_rel->partial_pathlist == NIL);
|
||||||
|
|
||||||
/* Convert subpath's pathkeys to outer representation */
|
/* Same for partial paths. */
|
||||||
pathkeys = convert_subquery_pathkeys(root,
|
foreach(lc, sub_final_rel->partial_pathlist)
|
||||||
rel,
|
{
|
||||||
subpath->pathkeys,
|
Path *subpath = (Path *) lfirst(lc);
|
||||||
make_tlist_from_pathtarget(subpath->pathtarget));
|
List *pathkeys;
|
||||||
|
|
||||||
/* Generate outer path using this subpath */
|
/* Convert subpath's pathkeys to outer representation */
|
||||||
add_partial_path(rel, (Path *)
|
pathkeys = convert_subquery_pathkeys(root,
|
||||||
create_subqueryscan_path(root, rel, subpath,
|
rel,
|
||||||
pathkeys, required_outer));
|
subpath->pathkeys,
|
||||||
|
make_tlist_from_pathtarget(subpath->pathtarget));
|
||||||
|
|
||||||
|
/* Generate outer path using this subpath */
|
||||||
|
add_partial_path(rel, (Path *)
|
||||||
|
create_subqueryscan_path(root, rel, subpath,
|
||||||
|
pathkeys,
|
||||||
|
required_outer));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -330,7 +330,8 @@ recurse_set_operations(Node *setOp, PlannerInfo *root,
|
|||||||
* to build a partial path for this relation. But there's no point in
|
* to build a partial path for this relation. But there's no point in
|
||||||
* considering any path but the cheapest.
|
* considering any path but the cheapest.
|
||||||
*/
|
*/
|
||||||
if (final_rel->partial_pathlist != NIL)
|
if (rel->consider_parallel && bms_is_empty(rel->lateral_relids) &&
|
||||||
|
final_rel->partial_pathlist != NIL)
|
||||||
{
|
{
|
||||||
Path *partial_subpath;
|
Path *partial_subpath;
|
||||||
Path *partial_path;
|
Path *partial_path;
|
||||||
|
@ -770,6 +770,12 @@ add_partial_path(RelOptInfo *parent_rel, Path *new_path)
|
|||||||
/* Check for query cancel. */
|
/* Check for query cancel. */
|
||||||
CHECK_FOR_INTERRUPTS();
|
CHECK_FOR_INTERRUPTS();
|
||||||
|
|
||||||
|
/* Path to be added must be parallel safe. */
|
||||||
|
Assert(new_path->parallel_safe);
|
||||||
|
|
||||||
|
/* Relation should be OK for parallelism, too. */
|
||||||
|
Assert(parent_rel->consider_parallel);
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* As in add_path, throw out any paths which are dominated by the new
|
* As in add_path, throw out any paths which are dominated by the new
|
||||||
* path, but throw out the new path if some existing path dominates it.
|
* path, but throw out the new path if some existing path dominates it.
|
||||||
|
@ -955,4 +955,23 @@ ORDER BY 1, 2, 3;
|
|||||||
------------------------------+---------------------------+-------------+--------------
|
------------------------------+---------------------------+-------------+--------------
|
||||||
(0 rows)
|
(0 rows)
|
||||||
|
|
||||||
|
-- test interation between subquery and partial_paths
|
||||||
|
SET LOCAL min_parallel_table_scan_size TO 0;
|
||||||
|
CREATE VIEW tenk1_vw_sec WITH (security_barrier) AS SELECT * FROM tenk1;
|
||||||
|
EXPLAIN (COSTS OFF)
|
||||||
|
SELECT 1 FROM tenk1_vw_sec WHERE EXISTS (SELECT 1 WHERE unique1 = 0);
|
||||||
|
QUERY PLAN
|
||||||
|
-------------------------------------------------------------------
|
||||||
|
Subquery Scan on tenk1_vw_sec
|
||||||
|
Filter: (alternatives: SubPlan 1 or hashed SubPlan 2)
|
||||||
|
-> Gather
|
||||||
|
Workers Planned: 4
|
||||||
|
-> Parallel Index Only Scan using tenk1_unique1 on tenk1
|
||||||
|
SubPlan 1
|
||||||
|
-> Result
|
||||||
|
One-Time Filter: (tenk1_vw_sec.unique1 = 0)
|
||||||
|
SubPlan 2
|
||||||
|
-> Result
|
||||||
|
(10 rows)
|
||||||
|
|
||||||
rollback;
|
rollback;
|
||||||
|
@ -383,4 +383,10 @@ ORDER BY 1;
|
|||||||
SELECT * FROM information_schema.foreign_data_wrapper_options
|
SELECT * FROM information_schema.foreign_data_wrapper_options
|
||||||
ORDER BY 1, 2, 3;
|
ORDER BY 1, 2, 3;
|
||||||
|
|
||||||
|
-- test interation between subquery and partial_paths
|
||||||
|
SET LOCAL min_parallel_table_scan_size TO 0;
|
||||||
|
CREATE VIEW tenk1_vw_sec WITH (security_barrier) AS SELECT * FROM tenk1;
|
||||||
|
EXPLAIN (COSTS OFF)
|
||||||
|
SELECT 1 FROM tenk1_vw_sec WHERE EXISTS (SELECT 1 WHERE unique1 = 0);
|
||||||
|
|
||||||
rollback;
|
rollback;
|
||||||
|
Loading…
x
Reference in New Issue
Block a user