diff options
Diffstat (limited to 'src')
-rw-r--r-- | src/backend/optimizer/path/allpaths.c | 4 | ||||
-rw-r--r-- | src/backend/optimizer/plan/createplan.c | 54 | ||||
-rw-r--r-- | src/test/regress/expected/matview.out | 12 | ||||
-rw-r--r-- | src/test/regress/expected/privileges.out | 5 | ||||
-rw-r--r-- | src/test/regress/sql/matview.sql | 7 | ||||
-rw-r--r-- | src/test/regress/sql/privileges.sql | 4 |
6 files changed, 52 insertions, 34 deletions
diff --git a/src/backend/optimizer/path/allpaths.c b/src/backend/optimizer/path/allpaths.c index 86d5bb71b0a..474cd7b6eba 100644 --- a/src/backend/optimizer/path/allpaths.c +++ b/src/backend/optimizer/path/allpaths.c @@ -1159,7 +1159,9 @@ set_subquery_pathlist(PlannerInfo *root, RelOptInfo *rel, /* * It's possible that constraint exclusion proved the subquery empty. If * so, it's convenient to turn it back into a dummy path so that we will - * recognize appropriate optimizations at this level. + * recognize appropriate optimizations at this query level. (But see + * create_append_plan in createplan.c, which has to reverse this + * substitution.) */ if (is_dummy_plan(rel->subplan)) { diff --git a/src/backend/optimizer/plan/createplan.c b/src/backend/optimizer/plan/createplan.c index 65a2e38fc33..e43ef3aa898 100644 --- a/src/backend/optimizer/plan/createplan.c +++ b/src/backend/optimizer/plan/createplan.c @@ -678,30 +678,49 @@ static Plan * create_append_plan(PlannerInfo *root, AppendPath *best_path) { Append *plan; - List *tlist = build_relation_tlist(best_path->path.parent); + RelOptInfo *rel = best_path->path.parent; + List *tlist = build_relation_tlist(rel); List *subplans = NIL; ListCell *subpaths; /* - * It is possible for the subplans list to contain only one entry, or even - * no entries. Handle these cases specially. + * The subpaths list could be empty, if every child was proven empty by + * constraint exclusion. In that case generate a dummy plan that returns + * no rows. * - * XXX ideally, if there's just one entry, we'd not bother to generate an - * Append node but just return the single child. At the moment this does - * not work because the varno of the child scan plan won't match the - * parent-rel Vars it'll be asked to emit. + * Note that an AppendPath with no members is also generated in certain + * cases where there was no appending construct at all, but we know the + * relation is empty (see set_dummy_rel_pathlist). */ if (best_path->subpaths == NIL) { - /* Generate a Result plan with constant-FALSE gating qual */ - return (Plan *) make_result(root, - tlist, - (Node *) list_make1(makeBoolConst(false, - false)), - NULL); + /* + * If this is a dummy path for a subquery, we have to wrap the + * subquery's original plan in a SubqueryScan so that setrefs.c will + * do the right things. (In particular, it must pull up the + * subquery's rangetable so that the executor will apply permissions + * checks to those rels at runtime.) + */ + if (rel->rtekind == RTE_SUBQUERY) + { + Assert(is_dummy_plan(rel->subplan)); + return (Plan *) make_subqueryscan(tlist, + NIL, + rel->relid, + rel->subplan); + } + else + { + /* Generate a Result plan with constant-FALSE gating qual */ + return (Plan *) make_result(root, + tlist, + (Node *) list_make1(makeBoolConst(false, + false)), + NULL); + } } - /* Normal case with multiple subpaths */ + /* Build the plan for each child */ foreach(subpaths, best_path->subpaths) { Path *subpath = (Path *) lfirst(subpaths); @@ -709,6 +728,13 @@ create_append_plan(PlannerInfo *root, AppendPath *best_path) subplans = lappend(subplans, create_plan_recurse(root, subpath)); } + /* + * XXX ideally, if there's just one child, we'd not bother to generate an + * Append node but just return the single child. At the moment this does + * not work because the varno of the child scan plan won't match the + * parent-rel Vars it'll be asked to emit. + */ + plan = make_append(subplans, tlist); return (Plan *) plan; diff --git a/src/test/regress/expected/matview.out b/src/test/regress/expected/matview.out index bda4d168aa4..5a53f84c5b8 100644 --- a/src/test/regress/expected/matview.out +++ b/src/test/regress/expected/matview.out @@ -418,15 +418,3 @@ NOTICE: drop cascades to 3 other objects DETAIL: drop cascades to view v_test2 drop cascades to materialized view mv_test2 drop cascades to materialized view mv_test3 --- test a corner case for "with no data" versus a query which yields no rows -CREATE MATERIALIZED VIEW matview_unit_false AS SELECT false WHERE false WITH NO DATA; -SELECT * FROM matview_unit_false; -ERROR: materialized view "matview_unit_false" has not been populated -HINT: Use the REFRESH MATERIALIZED VIEW command. -REFRESH MATERIALIZED VIEW matview_unit_false; -SELECT * FROM matview_unit_false; - bool ------- -(0 rows) - -DROP MATERIALIZED VIEW matview_unit_false; diff --git a/src/test/regress/expected/privileges.out b/src/test/regress/expected/privileges.out index e8930cb2eb5..68afecc91f7 100644 --- a/src/test/regress/expected/privileges.out +++ b/src/test/regress/expected/privileges.out @@ -198,6 +198,8 @@ CREATE VIEW atestv1 AS SELECT * FROM atest1; -- ok /* The next *should* fail, but it's not implemented that way yet. */ CREATE VIEW atestv2 AS SELECT * FROM atest2; CREATE VIEW atestv3 AS SELECT * FROM atest3; -- ok +/* Empty view is a corner case that failed in 9.2. */ +CREATE VIEW atestv0 AS SELECT 0 as x WHERE false; -- ok SELECT * FROM atestv1; -- ok a | b ---+----- @@ -224,6 +226,8 @@ SELECT * FROM atestv3; -- ok -----+-----+------- (0 rows) +SELECT * FROM atestv0; -- fail +ERROR: permission denied for relation atestv0 CREATE VIEW atestv4 AS SELECT * FROM atestv3; -- nested view SELECT * FROM atestv4; -- ok one | two | three @@ -1386,6 +1390,7 @@ drop table dep_priv_test; drop sequence x_seq; DROP FUNCTION testfunc2(int); DROP FUNCTION testfunc4(boolean); +DROP VIEW atestv0; DROP VIEW atestv1; DROP VIEW atestv2; -- this should cascade to drop atestv4 diff --git a/src/test/regress/sql/matview.sql b/src/test/regress/sql/matview.sql index 88c4d220aff..08b48188c3c 100644 --- a/src/test/regress/sql/matview.sql +++ b/src/test/regress/sql/matview.sql @@ -129,10 +129,3 @@ CREATE MATERIALIZED VIEW mv_test3 AS SELECT * FROM mv_test2 WHERE moo = 12345; SELECT pg_relation_is_scannable('mv_test3'::regclass); DROP VIEW v_test1 CASCADE; - --- test a corner case for "with no data" versus a query which yields no rows -CREATE MATERIALIZED VIEW matview_unit_false AS SELECT false WHERE false WITH NO DATA; -SELECT * FROM matview_unit_false; -REFRESH MATERIALIZED VIEW matview_unit_false; -SELECT * FROM matview_unit_false; -DROP MATERIALIZED VIEW matview_unit_false; diff --git a/src/test/regress/sql/privileges.sql b/src/test/regress/sql/privileges.sql index d4d328e649f..6ac3378a8d0 100644 --- a/src/test/regress/sql/privileges.sql +++ b/src/test/regress/sql/privileges.sql @@ -147,6 +147,8 @@ CREATE VIEW atestv1 AS SELECT * FROM atest1; -- ok /* The next *should* fail, but it's not implemented that way yet. */ CREATE VIEW atestv2 AS SELECT * FROM atest2; CREATE VIEW atestv3 AS SELECT * FROM atest3; -- ok +/* Empty view is a corner case that failed in 9.2. */ +CREATE VIEW atestv0 AS SELECT 0 as x WHERE false; -- ok SELECT * FROM atestv1; -- ok SELECT * FROM atestv2; -- fail @@ -158,6 +160,7 @@ SET SESSION AUTHORIZATION regressuser4; SELECT * FROM atestv1; -- ok SELECT * FROM atestv2; -- fail SELECT * FROM atestv3; -- ok +SELECT * FROM atestv0; -- fail CREATE VIEW atestv4 AS SELECT * FROM atestv3; -- nested view SELECT * FROM atestv4; -- ok @@ -828,6 +831,7 @@ drop sequence x_seq; DROP FUNCTION testfunc2(int); DROP FUNCTION testfunc4(boolean); +DROP VIEW atestv0; DROP VIEW atestv1; DROP VIEW atestv2; -- this should cascade to drop atestv4 |