diff --git a/ql/src/java/org/apache/hadoop/hive/ql/parse/CalcitePlanner.java b/ql/src/java/org/apache/hadoop/hive/ql/parse/CalcitePlanner.java index 5150de01baac..8353a0eeda51 100644 --- a/ql/src/java/org/apache/hadoop/hive/ql/parse/CalcitePlanner.java +++ b/ql/src/java/org/apache/hadoop/hive/ql/parse/CalcitePlanner.java @@ -79,6 +79,7 @@ import org.apache.calcite.rel.RelFieldCollation; import org.apache.calcite.rel.RelNode; import org.apache.calcite.rel.RelVisitor; +import org.apache.calcite.rel.SingleRel; import org.apache.calcite.rel.convert.ConverterImpl; import org.apache.calcite.rel.core.Aggregate; import org.apache.calcite.rel.core.AggregateCall; @@ -4918,15 +4919,12 @@ private RelNode genLogicalPlan(QB qb, boolean outerMostQB, aliasToRel.put(subqAlias, relNode); if (qb.getViewToTabSchema().containsKey(subqAlias)) { - if (relNode instanceof HiveProject) { - if (this.viewProjectToTableSchema == null) { - this.viewProjectToTableSchema = new LinkedHashMap<>(); - } - viewProjectToTableSchema.put((HiveProject) relNode, qb.getViewToTabSchema().get(subqAlias)); - } else { - throw new SemanticException("View " + subqAlias + " is corresponding to " - + relNode.toString() + ", rather than a HiveProject."); + HiveProject project = extractFirstProject(relNode) + .orElseThrow(() -> new SemanticException("Could not obtain a HiveProject from " + relNode)); + if (this.viewProjectToTableSchema == null) { + this.viewProjectToTableSchema = new LinkedHashMap<>(); } + viewProjectToTableSchema.put(project, qb.getViewToTabSchema().get(subqAlias)); } } @@ -5048,6 +5046,21 @@ private RelNode genLogicalPlan(QB qb, boolean outerMostQB, return srcRel; } + /** + * Extract the first HiveProject from a RelNode tree of SingleRel nodes. + * This doesn't search through inputs of multi-input nodes (like Joins). + * + * @param rel RelNode + * @return Optional HiveProject + */ + private Optional extractFirstProject(RelNode rel) { + return switch (rel) { + case HiveProject hiveProject -> Optional.of(hiveProject); + case SingleRel sr -> extractFirstProject(sr.getInput()); + case null, default -> Optional.empty(); + }; + } + private RelNode genGBHavingLogicalPlan(QB qb, RelNode srcRel) throws SemanticException { RelNode gbFilter = null; QBParseInfo qbp = getQBParseInfo(qb); diff --git a/ql/src/test/queries/clientpositive/view_top_relnode_not_project_authorization.q b/ql/src/test/queries/clientpositive/view_top_relnode_not_project_authorization.q new file mode 100644 index 000000000000..9f83328986cb --- /dev/null +++ b/ql/src/test/queries/clientpositive/view_top_relnode_not_project_authorization.q @@ -0,0 +1,16 @@ +set hive.security.authorization.enabled=true; +create table t1 (username string, id int); + +create view vw_t1 as select distinct username from t1 limit 5; +explain cbo select * from vw_t1; +select * from vw_t1; + +create view vw_t2 as +select username from (select username, id from t1 where id > 10 limit 1) x where username > 'a' order by id; +explain cbo select * from vw_t2; +select * from vw_t2; + +create view vw_t3 as +select username from (select username, id from t1 where id > 10 limit 10) x where username > 'a' limit 5; +explain cbo select * from vw_t3; +select * from vw_t3; diff --git a/ql/src/test/results/clientpositive/llap/view_top_relnode_not_project_authorization.q.out b/ql/src/test/results/clientpositive/llap/view_top_relnode_not_project_authorization.q.out new file mode 100644 index 000000000000..2bf398147b33 --- /dev/null +++ b/ql/src/test/results/clientpositive/llap/view_top_relnode_not_project_authorization.q.out @@ -0,0 +1,129 @@ +PREHOOK: query: create table t1 (username string, id int) +PREHOOK: type: CREATETABLE +PREHOOK: Output: database:default +PREHOOK: Output: default@t1 +POSTHOOK: query: create table t1 (username string, id int) +POSTHOOK: type: CREATETABLE +POSTHOOK: Output: database:default +POSTHOOK: Output: default@t1 +PREHOOK: query: create view vw_t1 as select distinct username from t1 limit 5 +PREHOOK: type: CREATEVIEW +PREHOOK: Input: default@t1 +PREHOOK: Output: database:default +PREHOOK: Output: default@vw_t1 +POSTHOOK: query: create view vw_t1 as select distinct username from t1 limit 5 +POSTHOOK: type: CREATEVIEW +POSTHOOK: Input: default@t1 +POSTHOOK: Output: database:default +POSTHOOK: Output: default@vw_t1 +POSTHOOK: Lineage: vw_t1.username SIMPLE [(t1)t1.FieldSchema(name:username, type:string, comment:null), ] +PREHOOK: query: explain cbo select * from vw_t1 +PREHOOK: type: QUERY +PREHOOK: Input: default@t1 +PREHOOK: Input: default@vw_t1 +#### A masked pattern was here #### +POSTHOOK: query: explain cbo select * from vw_t1 +POSTHOOK: type: QUERY +POSTHOOK: Input: default@t1 +POSTHOOK: Input: default@vw_t1 +#### A masked pattern was here #### +CBO PLAN: +HiveSortLimit(fetch=[5]) + HiveProject(username=[$0]) + HiveAggregate(group=[{0}]) + HiveTableScan(table=[[default, t1]], table:alias=[t1]) + +PREHOOK: query: select * from vw_t1 +PREHOOK: type: QUERY +PREHOOK: Input: default@t1 +PREHOOK: Input: default@vw_t1 +#### A masked pattern was here #### +POSTHOOK: query: select * from vw_t1 +POSTHOOK: type: QUERY +POSTHOOK: Input: default@t1 +POSTHOOK: Input: default@vw_t1 +#### A masked pattern was here #### +PREHOOK: query: create view vw_t2 as +select username from (select username, id from t1 where id > 10 limit 1) x where username > 'a' order by id +PREHOOK: type: CREATEVIEW +PREHOOK: Input: default@t1 +PREHOOK: Output: database:default +PREHOOK: Output: default@vw_t2 +POSTHOOK: query: create view vw_t2 as +select username from (select username, id from t1 where id > 10 limit 1) x where username > 'a' order by id +POSTHOOK: type: CREATEVIEW +POSTHOOK: Input: default@t1 +POSTHOOK: Output: database:default +POSTHOOK: Output: default@vw_t2 +POSTHOOK: Lineage: vw_t2.username SIMPLE [(t1)t1.FieldSchema(name:username, type:string, comment:null), ] +PREHOOK: query: explain cbo select * from vw_t2 +PREHOOK: type: QUERY +PREHOOK: Input: default@t1 +PREHOOK: Input: default@vw_t2 +#### A masked pattern was here #### +POSTHOOK: query: explain cbo select * from vw_t2 +POSTHOOK: type: QUERY +POSTHOOK: Input: default@t1 +POSTHOOK: Input: default@vw_t2 +#### A masked pattern was here #### +CBO PLAN: +HiveFilter(condition=[>($0, _UTF-16LE'a')]) + HiveProject(username=[$0]) + HiveSortLimit(fetch=[1]) + HiveProject(username=[$0]) + HiveFilter(condition=[>($1, 10)]) + HiveTableScan(table=[[default, t1]], table:alias=[t1]) + +PREHOOK: query: select * from vw_t2 +PREHOOK: type: QUERY +PREHOOK: Input: default@t1 +PREHOOK: Input: default@vw_t2 +#### A masked pattern was here #### +POSTHOOK: query: select * from vw_t2 +POSTHOOK: type: QUERY +POSTHOOK: Input: default@t1 +POSTHOOK: Input: default@vw_t2 +#### A masked pattern was here #### +PREHOOK: query: create view vw_t3 as +select username from (select username, id from t1 where id > 10 limit 10) x where username > 'a' limit 5 +PREHOOK: type: CREATEVIEW +PREHOOK: Input: default@t1 +PREHOOK: Output: database:default +PREHOOK: Output: default@vw_t3 +POSTHOOK: query: create view vw_t3 as +select username from (select username, id from t1 where id > 10 limit 10) x where username > 'a' limit 5 +POSTHOOK: type: CREATEVIEW +POSTHOOK: Input: default@t1 +POSTHOOK: Output: database:default +POSTHOOK: Output: default@vw_t3 +POSTHOOK: Lineage: vw_t3.username SIMPLE [(t1)t1.FieldSchema(name:username, type:string, comment:null), ] +PREHOOK: query: explain cbo select * from vw_t3 +PREHOOK: type: QUERY +PREHOOK: Input: default@t1 +PREHOOK: Input: default@vw_t3 +#### A masked pattern was here #### +POSTHOOK: query: explain cbo select * from vw_t3 +POSTHOOK: type: QUERY +POSTHOOK: Input: default@t1 +POSTHOOK: Input: default@vw_t3 +#### A masked pattern was here #### +CBO PLAN: +HiveSortLimit(fetch=[5]) + HiveProject(username=[$0]) + HiveFilter(condition=[>($0, _UTF-16LE'a')]) + HiveProject(username=[$0]) + HiveSortLimit(fetch=[10]) + HiveProject(username=[$0]) + HiveFilter(condition=[>($1, 10)]) + HiveTableScan(table=[[default, t1]], table:alias=[t1]) + +PREHOOK: query: select * from vw_t3 +PREHOOK: type: QUERY +PREHOOK: Input: default@t1 +PREHOOK: Input: default@vw_t3 +#### A masked pattern was here #### +POSTHOOK: query: select * from vw_t3 +POSTHOOK: type: QUERY +POSTHOOK: Input: default@t1 +POSTHOOK: Input: default@vw_t3 +#### A masked pattern was here ####