Skip to content

Commit

Permalink
MONDRIAN: Add Hive dialect. Fixes MONDRIAN-789, "Hive dialect for Mon…
Browse files Browse the repository at this point in the history
…drian".

    Contributed by Fu Hongwei. 

[git-p4: depot-paths = "//open/mondrian/": change = 14118]
  • Loading branch information
julianhyde committed Feb 14, 2011
1 parent 44873cf commit 50d76a8
Show file tree
Hide file tree
Showing 5 changed files with 397 additions and 18 deletions.
3 changes: 3 additions & 0 deletions demo/FoodMart.xml
Expand Up @@ -177,6 +177,9 @@ WHERE "product"."product_class_id" = "product_class"."product_class_id"
<NameExpression>
<SQL dialect="oracle">
"fname" || ' ' || "lname"
</SQL>
<SQL dialect="hive">
`customer`.`fullname`
</SQL>
<SQL dialect="hsqldb">
"fname" || ' ' || "lname"
Expand Down
203 changes: 189 additions & 14 deletions src/main/mondrian/rolap/sql/SqlQuery.java
Expand Up @@ -4,7 +4,7 @@
// Agreement, available at the following URL:
// http://www.eclipse.org/legal/epl-v10.html.
// Copyright (C) 2002-2002 Kana Software, Inc.
// Copyright (C) 2002-2009 Julian Hyde and others
// Copyright (C) 2002-2011 Julian Hyde and others
// All Rights Reserved.
// You must accept the terms of that agreement to use this software.
//
Expand Down Expand Up @@ -82,7 +82,8 @@ public class SqlQuery {
private boolean distinct;

private final ClauseList select;
private final ClauseList from;
private final FromClauseList from;
private final OnClauseList on;
private final ClauseList where;
private final ClauseList groupBy;
private final ClauseList having;
Expand Down Expand Up @@ -122,7 +123,8 @@ public SqlQuery(Dialect dialect, boolean formatted) {

// both select and from allow duplications
this.select = new ClauseList(true);
this.from = new ClauseList(true);
this.from = new FromClauseList(true);
this.on = new OnClauseList(false);

this.groupingFunction = new ClauseList(false);
this.where = new ClauseList(false);
Expand Down Expand Up @@ -286,6 +288,27 @@ boolean addFromTable(
return true;
}

/**
* Returns whether dialect accepts 'ON'.
* Only join conditions like A.a = B.b or Upper(A.a) = Upper(B.b)
* will be added to ON, or will be added to WHERE.
*/
private boolean acceptsOn(String exp) {
StringTokenizer st = new StringTokenizer(exp, "=");
if (st.countTokens() != 2) {
return false;
}
StringTokenizer st1 = new StringTokenizer(st.nextToken(), ".");
if (st1.countTokens() != 2) {
return false;
}
st1 = new StringTokenizer(st.nextToken(), ".");
if (st1.countTokens() != 2) {
return false;
}
return true;
}

public void addFrom(
final SqlQuery sqlQuery,
final String alias,
Expand Down Expand Up @@ -354,8 +377,13 @@ public boolean addFrom(
dialect.quoteIdentifier(buf, leftAlias, join.leftKey);
buf.append(" = ");
dialect.quoteIdentifier(buf, rightAlias, join.rightKey);

addWhere(buf.toString());
switch (dialect.getDatabaseProduct()) {
case HIVE:
addOn(buf.toString());
break;
default:
addWhere(buf.toString());
}
}
return added;

Expand Down Expand Up @@ -424,6 +452,17 @@ public String addSelect(final String expression, final String alias) {
return alias;
}

/**
* Adds an expression to ON join condition in FROM clause.
*/
public void addOn(final String expression) {
if (acceptsOn(expression)) {
on.add(expression);
} else {
addWhere(expression);
}
}

public void addWhere(
final String exprLeft,
final String exprMid,
Expand Down Expand Up @@ -511,7 +550,17 @@ public String toString()
buf,
distinct ? "select distinct " : "select ", ", ");
buf.append(getGroupingFunction(""));
from.toBuffer(buf, " from ", ", ");
switch (dialect.getDatabaseProduct()) {
case HIVE:
if (on.size() == 0) {
from.toBuffer(buf, " from ", " join ");
} else {
from.toBufferWithOn(buf, " from ", " join ", on);
}
break;
default:
from.toBuffer(buf, " from ", ", ");
}
where.toBuffer(buf, " where ", " and ");
if (hasGroupingSet()) {
StringWriter stringWriter = new StringWriter();
Expand Down Expand Up @@ -614,8 +663,136 @@ public void addGroupingFunction(String columnExpr) {
groupingFunction.add(columnExpr);
}

static class OnClauseList extends ClauseList {
private final List<String> leftTables = new ArrayList<String>();
private final List<String> rightTables = new ArrayList<String>();

OnClauseList(boolean allowDups) {
super(allowDups);
}

void toBuffer(
final List<String> addedTables,
final String current,
final StringBuilder buf)
{
int n = 0;
// first check when the current table is on the left side
for (int i = 0; i < leftTables.size(); i++) {
// the first table was added before join, it has to be handled
// specially: Table.column = expression
if (addedTables.size() == 1
&& addedTables.get(0).equals(leftTables.get(i))
&& leftTables.get(i).equals(rightTables.get(i)))
{
buf.append(n++ == 0 ? " on (" : " and ");
buf.append(get(i));
}
if (current.equals(leftTables.get(i))
&& addedTables.contains(rightTables.get(i)))
{
buf.append(n++ == 0 ? " on (" : " and ");
buf.append(get(i));
}
}
// then check when the current table is on the right side
for (int i = 0; i < rightTables.size(); i++) {
if (current.equals(rightTables.get(i))
&& addedTables.contains(leftTables.get(i)))
{
buf.append(n++ == 0 ? " on (" : " and ");
buf.append(get(i));
}
}
if (n > 0) {
buf.append(")");
}
}

private String stripFunctions(String str) {
StringTokenizer st = new StringTokenizer(str, "() ");
if (st.countTokens() == 1) {
return str;
} else {
String ret = st.nextToken();
while (st.hasMoreTokens()) {
ret = st.nextToken();
}
return ret;
}
}

public boolean add(String left, String right, String exp) {
assert leftTables.size() == rightTables.size();
StringTokenizer st;
if (super.add(exp)) {
// left is the form of 'Table'.'column'
if (left.contains(".")) {
left = stripFunctions(left);
st = new StringTokenizer(left, ".");
leftTables.add(st.nextToken().trim());
if (right.contains(".")) {
right = stripFunctions(right);
} else {
// Only left contains table name, Table.Column = abc
// store the same name for right tables
right = left;
}
st = new StringTokenizer(right, ".");
rightTables.add(st.nextToken().trim());
} else if (right.contains(".")) {
right = stripFunctions(right);
st = new StringTokenizer(right, ".");
String name = st.nextToken().trim();
leftTables.add(name);
rightTables.add(name);
}
return true;
} else {
return false;
}
}

public boolean add(String expression) {
StringTokenizer st = new StringTokenizer(expression, "=");
return add(st.nextToken(), st.nextToken(), expression);
}
}

static class FromClauseList extends ClauseList {

FromClauseList(boolean allowsDups) {
super(allowsDups);
}

void toBufferWithOn(
final StringBuilder buf,
final String first,
final String sep,
final OnClauseList on)
{
List<String> addedTables = new ArrayList<String>();
int n = 0;
for (String s : this) {
StringTokenizer st = new StringTokenizer(s, " ");
// this is correct only when alias is the same with table name
String str = st.nextToken();
if (n++ == 0) {
buf.append(first);
buf.append(s);
} else {
buf.append(sep);
buf.append(s);
// adding "ON (a = b ,...)" to the clause
on.toBuffer(addedTables, str, buf);
}
addedTables.add(str);
}
}
}

static class ClauseList extends ArrayList<String> {
private final boolean allowDups;
protected final boolean allowDups;

ClauseList(final boolean allowDups) {
this.allowDups = allowDups;
Expand All @@ -642,11 +819,10 @@ void toBuffer(
final String first,
final String sep)
{
boolean firstTime = true;
int n = 0;
for (String s : this) {
if (firstTime) {
if (n++ == 0) {
buf.append(first);
firstTime = false;
} else {
buf.append(sep);
}
Expand Down Expand Up @@ -674,14 +850,13 @@ void print(
final String last)
{
String subprefix = prefix + " ";
boolean firstTime = true;
int n = 0;
for (String s : this) {
if (firstTime) {
if (n++ == 0) {
if (generateFormattedSql) {
pw.print(prefix);
}
pw.print(first);
firstTime = false;
} else {
pw.print(sep);
}
Expand All @@ -695,7 +870,7 @@ void print(
}
}
pw.print(last);
if (!firstTime && generateFormattedSql) {
if (n > 0 && generateFormattedSql) {
pw.println();
}
}
Expand Down
3 changes: 2 additions & 1 deletion src/main/mondrian/spi/Dialect.java
Expand Up @@ -2,7 +2,7 @@
// This software is subject to the terms of the Eclipse Public License v1.0
// Agreement, available at the following URL:
// http://www.eclipse.org/legal/epl-v10.html.
// Copyright (C) 2008-2009 Julian Hyde
// Copyright (C) 2008-2011 Julian Hyde
// All Rights Reserved.
// You must accept the terms of that agreement to use this software.
*/
Expand Down Expand Up @@ -723,6 +723,7 @@ enum DatabaseProduct {
DB2,
FIREBIRD,
GREENPLUM,
HIVE,
HSQLDB,
INFORMIX,
INFOBRIGHT,
Expand Down

0 comments on commit 50d76a8

Please sign in to comment.