Index: ql/src/test/results/clientpositive/join_view.q.out =================================================================== --- ql/src/test/results/clientpositive/join_view.q.out (revision 0) +++ ql/src/test/results/clientpositive/join_view.q.out (revision 0) @@ -0,0 +1,147 @@ +PREHOOK: query: drop table invites +PREHOOK: type: DROPTABLE +POSTHOOK: query: drop table invites +POSTHOOK: type: DROPTABLE +PREHOOK: query: drop table invites2 +PREHOOK: type: DROPTABLE +POSTHOOK: query: drop table invites2 +POSTHOOK: type: DROPTABLE +PREHOOK: query: create table invites (foo int, bar string) partitioned by (ds string) +PREHOOK: type: CREATETABLE +POSTHOOK: query: create table invites (foo int, bar string) partitioned by (ds string) +POSTHOOK: type: CREATETABLE +POSTHOOK: Output: default@invites +PREHOOK: query: create table invites2 (foo int, bar string) partitioned by (ds string) +PREHOOK: type: CREATETABLE +POSTHOOK: query: create table invites2 (foo int, bar string) partitioned by (ds string) +POSTHOOK: type: CREATETABLE +POSTHOOK: Output: default@invites2 +PREHOOK: query: -- test join views: see HIVE-1989 + +create view v as select invites.bar, invites2.foo, invites2.ds from invites join invites2 on invites.ds=invites2.ds +PREHOOK: type: CREATEVIEW +PREHOOK: Output: file:/var/folders/nt/ng21tg0n1jl4547lw0k8lg6hq_nw87/T/charleschen/hive_2011-09-01_22-51-19_156_3637363787057278672/-mr-10000 +POSTHOOK: query: -- test join views: see HIVE-1989 + +create view v as select invites.bar, invites2.foo, invites2.ds from invites join invites2 on invites.ds=invites2.ds +POSTHOOK: type: CREATEVIEW +POSTHOOK: Output: default@v +POSTHOOK: Output: file:/var/folders/nt/ng21tg0n1jl4547lw0k8lg6hq_nw87/T/charleschen/hive_2011-09-01_22-51-19_156_3637363787057278672/-mr-10000 +PREHOOK: query: explain select * from v where ds='2011-09-01' +PREHOOK: type: QUERY +POSTHOOK: query: explain select * from v where ds='2011-09-01' +POSTHOOK: type: QUERY +ABSTRACT SYNTAX TREE: + (TOK_QUERY (TOK_FROM (TOK_TABREF (TOK_TABNAME v))) (TOK_INSERT (TOK_DESTINATION (TOK_DIR TOK_TMP_FILE)) (TOK_SELECT (TOK_SELEXPR TOK_ALLCOLREF)) (TOK_WHERE (= (TOK_TABLE_OR_COL ds) '2011-09-01')))) + +STAGE DEPENDENCIES: + Stage-1 is a root stage + Stage-0 is a root stage + +STAGE PLANS: + Stage: Stage-1 + Map Reduce + Alias -> Map Operator Tree: + v:invites + TableScan + alias: invites + Filter Operator + predicate: + expr: (ds = '2011-09-01') + type: boolean + Reduce Output Operator + key expressions: + expr: ds + type: string + sort order: + + Map-reduce partition columns: + expr: ds + type: string + tag: 0 + value expressions: + expr: bar + type: string + v:invites2 + TableScan + alias: invites2 + Filter Operator + predicate: + expr: (ds = '2011-09-01') + type: boolean + Reduce Output Operator + key expressions: + expr: ds + type: string + sort order: + + Map-reduce partition columns: + expr: ds + type: string + tag: 1 + value expressions: + expr: foo + type: int + expr: ds + type: string + Reduce Operator Tree: + Join Operator + condition map: + Inner Join 0 to 1 + condition expressions: + 0 {VALUE._col1} + 1 {VALUE._col0} {VALUE._col2} + handleSkewJoin: false + outputColumnNames: _col1, _col5, _col7 + Select Operator + expressions: + expr: _col1 + type: string + expr: _col5 + type: int + expr: _col7 + type: string + outputColumnNames: _col0, _col1, _col2 + Select Operator + expressions: + expr: _col0 + type: string + expr: _col1 + type: int + expr: _col2 + type: string + outputColumnNames: _col0, _col1, _col2 + File Output Operator + compressed: false + GlobalTableId: 0 + table: + input format: org.apache.hadoop.mapred.TextInputFormat + output format: org.apache.hadoop.hive.ql.io.HiveIgnoreKeyTextOutputFormat + + Stage: Stage-0 + Fetch Operator + limit: -1 + + +PREHOOK: query: drop view v +PREHOOK: type: DROPVIEW +PREHOOK: Input: default@v +PREHOOK: Output: default@v +POSTHOOK: query: drop view v +POSTHOOK: type: DROPVIEW +POSTHOOK: Input: default@v +POSTHOOK: Output: default@v +PREHOOK: query: drop table invites +PREHOOK: type: DROPTABLE +PREHOOK: Input: default@invites +PREHOOK: Output: default@invites +POSTHOOK: query: drop table invites +POSTHOOK: type: DROPTABLE +POSTHOOK: Input: default@invites +POSTHOOK: Output: default@invites +PREHOOK: query: drop table invites2 +PREHOOK: type: DROPTABLE +PREHOOK: Input: default@invites2 +PREHOOK: Output: default@invites2 +POSTHOOK: query: drop table invites2 +POSTHOOK: type: DROPTABLE +POSTHOOK: Input: default@invites2 +POSTHOOK: Output: default@invites2 Index: ql/src/test/queries/clientpositive/join_view.q =================================================================== --- ql/src/test/queries/clientpositive/join_view.q (revision 0) +++ ql/src/test/queries/clientpositive/join_view.q (revision 0) @@ -0,0 +1,16 @@ +drop table invites; +drop table invites2; +create table invites (foo int, bar string) partitioned by (ds string); +create table invites2 (foo int, bar string) partitioned by (ds string); + +set hive.mapred.mode=strict; + +-- test join views: see HIVE-1989 + +create view v as select invites.bar, invites2.foo, invites2.ds from invites join invites2 on invites.ds=invites2.ds; + +explain select * from v where ds='2011-09-01'; + +drop view v; +drop table invites; +drop table invites2; \ No newline at end of file Index: ql/src/java/org/apache/hadoop/hive/ql/parse/UnparseTranslator.java =================================================================== --- ql/src/java/org/apache/hadoop/hive/ql/parse/UnparseTranslator.java (revision 1165909) +++ ql/src/java/org/apache/hadoop/hive/ql/parse/UnparseTranslator.java (working copy) @@ -58,9 +58,12 @@ /** * Register a translation to be performed as part of unparse. - * The translation must not overlap with any previously - * registered translations (unless it is identical to an - * existing translation, in which case it is ignored). + * If the translation overlaps with any previously + * registered translation, then it must be either + * identical or a prefix (in which cases it is ignored), + * or else it must extend the existing translation (i.e. + * the existing translation must be a prefix of the new translation). + * All other overlap cases result in assertion failures. * * @param node * target node whose subtree is to be replaced @@ -87,10 +90,11 @@ translation.tokenStopIndex = tokenStopIndex; translation.replacementText = replacementText; - // Sanity check: no overlap with regions already being expanded + // Sanity check for overlap with regions already being expanded assert (tokenStopIndex >= tokenStartIndex); Map.Entry existingEntry; existingEntry = translations.floorEntry(tokenStartIndex); + boolean prefix = false; if (existingEntry != null) { if (existingEntry.getKey().equals(tokenStartIndex)) { if (existingEntry.getValue().tokenStopIndex == tokenStopIndex) { @@ -99,16 +103,32 @@ // redundant, but we'll let it pass return; } + } else if (tokenStopIndex > existingEntry.getValue().tokenStopIndex) { + // is existing mapping a prefix for new mapping? if so, that's also + // redundant, but in this case we need to expand it + prefix = replacementText.startsWith( + existingEntry.getValue().replacementText); + assert(prefix); + } else { + // new mapping is a prefix for existing mapping: ignore it + prefix = existingEntry.getValue().replacementText.startsWith( + replacementText); + assert(prefix); + return; } } - assert (existingEntry.getValue().tokenStopIndex < tokenStartIndex); + if (!prefix) { + assert (existingEntry.getValue().tokenStopIndex < tokenStartIndex); + } } - existingEntry = translations.ceilingEntry(tokenStartIndex); - if (existingEntry != null) { - assert (existingEntry.getKey() > tokenStopIndex); + if (!prefix) { + existingEntry = translations.ceilingEntry(tokenStartIndex); + if (existingEntry != null) { + assert (existingEntry.getKey() > tokenStopIndex); + } } - // It's all good: create a new entry in the map + // It's all good: create a new entry in the map (or update existing one) translations.put(tokenStartIndex, translation); }