kylin-commits mailing list archives

Site index · List index
Message view « Date » · « Thread »
Top « Date » · « Thread »
From luke...@apache.org
Subject [09/51] [partial] incubator-kylin git commit: migrate repo from github.com to apache git
Date Wed, 07 Jan 2015 14:46:30 GMT
http://git-wip-us.apache.org/repos/asf/incubator-kylin/blob/4b631f92/query/src/main/java/com/kylinolap/query/enumerator/CubeEnumerator.java
----------------------------------------------------------------------
diff --git a/query/src/main/java/com/kylinolap/query/enumerator/CubeEnumerator.java b/query/src/main/java/com/kylinolap/query/enumerator/CubeEnumerator.java
new file mode 100644
index 0000000..5768e60
--- /dev/null
+++ b/query/src/main/java/com/kylinolap/query/enumerator/CubeEnumerator.java
@@ -0,0 +1,223 @@
+/*
+ * Copyright 2013-2014 eBay Software Foundation
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.kylinolap.query.enumerator;
+
+import java.util.Collection;
+import java.util.HashSet;
+import java.util.List;
+import java.util.Map;
+import java.util.Properties;
+
+import net.hydromatic.linq4j.Enumerator;
+import net.hydromatic.optiq.DataContext;
+import net.hydromatic.optiq.jdbc.OptiqConnection;
+
+import org.eigenbase.reltype.RelDataTypeField;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import com.kylinolap.metadata.model.cube.DimensionDesc;
+import com.kylinolap.metadata.model.cube.FunctionDesc;
+import com.kylinolap.metadata.model.cube.MeasureDesc;
+import com.kylinolap.metadata.model.cube.TblColRef;
+import com.kylinolap.query.relnode.OLAPContext;
+import com.kylinolap.storage.IStorageEngine;
+import com.kylinolap.storage.StorageEngineFactory;
+import com.kylinolap.storage.filter.CompareTupleFilter;
+import com.kylinolap.storage.filter.TupleFilter;
+import com.kylinolap.storage.tuple.ITuple;
+import com.kylinolap.storage.tuple.ITupleIterator;
+
+/**
+ * @author xjiang
+ */
+public class CubeEnumerator implements Enumerator<Object[]> {
+
+    private final static Logger logger = LoggerFactory.getLogger(CubeEnumerator.class);
+
+    private final OLAPContext olapContext;
+    private final DataContext optiqContext;
+    private final Object[] current;
+    private ITupleIterator cursor;
+    private int[] fieldIndexes;
+
+    public CubeEnumerator(OLAPContext olapContext, DataContext optiqContext) {
+        this.olapContext = olapContext;
+        this.optiqContext = optiqContext;
+        this.current = new Object[olapContext.olapRowType.getFieldCount()];
+        this.cursor = null;
+        this.fieldIndexes = null;
+    }
+
+    @Override
+    public Object[] current() {
+        return current;
+    }
+
+    @Override
+    public boolean moveNext() {
+        if (cursor == null) {
+            cursor = queryStorage();
+        }
+
+        if (!cursor.hasNext()) {
+            return false;
+        }
+
+        ITuple tuple = cursor.next();
+        if (tuple == null) {
+            return false;
+        }
+        convertCurrentRow(tuple);
+        return true;
+    }
+
+    @Override
+    public void reset() {
+        close();
+        cursor = queryStorage();
+    }
+
+    @Override
+    public void close() {
+        if (cursor != null) {
+            cursor.close();
+        }
+    }
+
+    private Object[] convertCurrentRow(ITuple tuple) {
+
+        // build field index map
+        if (this.fieldIndexes == null) {
+            List<String> fields = tuple.getAllFields();
+            int size = fields.size();
+            this.fieldIndexes = new int[size];
+            for (int i = 0; i < size; i++) {
+                String field = fields.get(i);
+                RelDataTypeField relField = olapContext.olapRowType.getField(field, true);
+                if (relField != null) {
+                    fieldIndexes[i] = relField.getIndex();
+                } else {
+                    fieldIndexes[i] = -1;
+                }
+            }
+        }
+
+        // set field value
+        Object[] values = tuple.getAllValues();
+        for (int i = 0, n = values.length; i < n; i++) {
+            Object value = values[i];
+            int index = fieldIndexes[i];
+            if (index >= 0) {
+                current[index] = value;
+            }
+        }
+
+        return current;
+    }
+
+    private ITupleIterator queryStorage() {
+        logger.debug("query storage...");
+
+        // set connection properties
+        setConnectionProperties();
+
+        // bind dynamic variables
+        bindVariable(olapContext.filter);
+
+        // build dimension & metrics
+        Collection<TblColRef> dimensions = new HashSet<TblColRef>();
+        Collection<FunctionDesc> metrics = new HashSet<FunctionDesc>();
+        buildDimensionsAndMetrics(dimensions, metrics);
+
+        // query storage engine
+        IStorageEngine storageEngine = StorageEngineFactory.getStorageEngine(olapContext.cubeInstance);
+        ITupleIterator iterator = storageEngine.search(dimensions, olapContext.filter, olapContext.groupByColumns, metrics, olapContext.storageContext);
+        if (logger.isDebugEnabled()) {
+            logger.debug("return TupleIterator...");
+        }
+
+        this.fieldIndexes = null;
+        return iterator;
+    }
+
+    private void buildDimensionsAndMetrics(Collection<TblColRef> dimensions, Collection<FunctionDesc> metrics) {
+
+        for (FunctionDesc func : olapContext.aggregations) {
+            if (!func.isAppliedOnDimension()) {
+                metrics.add(func);
+            }
+        }
+
+        if (olapContext.isSimpleQuery()) {
+            // In order to prevent coprocessor from doing the real aggregating,
+            // All dimensions are injected
+            for (DimensionDesc dim : olapContext.cubeDesc.getDimensions()) {
+                for (TblColRef col : dim.getColumnRefs()) {
+                    dimensions.add(col);
+                }
+            }
+            // select sth from fact table
+            for (MeasureDesc measure : olapContext.cubeDesc.getMeasures()) {
+                FunctionDesc func = measure.getFunction();
+                if (func.isSum()) {
+                    // the rewritten name for sum(metric) is metric itself
+                    metrics.add(func);
+                }
+            }
+            olapContext.storageContext.markAvoidAggregation();
+        } else {
+            for (TblColRef column : olapContext.allColumns) {
+                // skip measure columns
+                if (olapContext.metricsColumns.contains(column)) {
+                    continue;
+                }
+                dimensions.add(column);
+            }
+        }
+    }
+
+    private void bindVariable(TupleFilter filter) {
+        if (filter == null) {
+            return;
+        }
+
+        for (TupleFilter childFilter : filter.getChildren()) {
+            bindVariable(childFilter);
+        }
+
+        if (filter instanceof CompareTupleFilter && optiqContext != null) {
+            CompareTupleFilter compFilter = (CompareTupleFilter) filter;
+            for (Map.Entry<String, String> entry : compFilter.getVariables().entrySet()) {
+                String variable = entry.getKey();
+                Object value = optiqContext.get(variable);
+                if (value != null) {
+                    compFilter.bindVariable(variable, value.toString());
+                }
+
+            }
+        }
+    }
+
+    private void setConnectionProperties() {
+        OptiqConnection conn = (OptiqConnection) optiqContext.getQueryProvider();
+        Properties connProps = conn.getProperties();
+
+        String propThreshold = connProps.getProperty(OLAPQuery.PROP_SCAN_THRESHOLD);
+        int threshold = Integer.valueOf(propThreshold);
+        olapContext.storageContext.setThreshold(threshold);
+    }
+}

http://git-wip-us.apache.org/repos/asf/incubator-kylin/blob/4b631f92/query/src/main/java/com/kylinolap/query/enumerator/HiveEnumerator.java
----------------------------------------------------------------------
diff --git a/query/src/main/java/com/kylinolap/query/enumerator/HiveEnumerator.java b/query/src/main/java/com/kylinolap/query/enumerator/HiveEnumerator.java
new file mode 100644
index 0000000..a5914a8
--- /dev/null
+++ b/query/src/main/java/com/kylinolap/query/enumerator/HiveEnumerator.java
@@ -0,0 +1,130 @@
+/*
+ * Copyright 2013-2014 eBay Software Foundation
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.kylinolap.query.enumerator;
+
+import java.sql.Connection;
+import java.sql.DriverManager;
+import java.sql.ResultSet;
+import java.sql.SQLException;
+import java.sql.Statement;
+
+import net.hydromatic.linq4j.Enumerator;
+
+import org.eigenbase.reltype.RelDataTypeField;
+
+import com.kylinolap.query.relnode.OLAPContext;
+
+/**
+ * Hive Query Result Enumerator
+ * 
+ * @author xjiang
+ * 
+ */
+public class HiveEnumerator implements Enumerator<Object[]> {
+
+    private final OLAPContext olapContext;
+    private final Object[] current;
+    private ResultSet rs;
+    private Connection conn;
+
+    public HiveEnumerator(OLAPContext olapContext) {
+        this.olapContext = olapContext;
+        this.current = new Object[olapContext.olapRowType.getFieldCount()];
+    }
+
+    @Override
+    public Object[] current() {
+        return current;
+    }
+
+    @Override
+    public boolean moveNext() {
+        if (rs == null) {
+            rs = executeQuery();
+        }
+        return populateResult();
+    }
+
+    private ResultSet executeQuery() {
+        String url = olapContext.olapSchema.getStarSchemaUrl();
+        String user = olapContext.olapSchema.getStarSchemaUser();
+        String pwd = olapContext.olapSchema.getStarSchemaPassword();
+        String sql = olapContext.sql;
+        Statement stmt = null;
+        try {
+            conn = DriverManager.getConnection(url, user, pwd);
+            stmt = conn.createStatement();
+            return stmt.executeQuery(sql);
+        } catch (SQLException e) {
+            throw new IllegalStateException(url + " can't execute query " + sql, e);
+        } finally {
+            if (stmt != null) {
+                try {
+                    stmt.close();
+                } catch (SQLException ex) {
+                    ex.printStackTrace();
+                }
+            }
+            stmt = null;
+            if (conn != null) {
+                try {
+                    conn.close();
+                } catch (SQLException ex) {
+                    ex.printStackTrace();
+                }
+                conn = null;
+            }
+        }
+    }
+
+    private boolean populateResult() {
+        try {
+            boolean hasNext = rs.next();
+            if (hasNext) {
+                for (RelDataTypeField relField : olapContext.olapRowType.getFieldList()) {
+                    Object value = rs.getObject(relField.getName().toLowerCase());
+                    current[relField.getIndex()] = value;
+                }
+            }
+            return hasNext;
+        } catch (SQLException e) {
+            throw new IllegalStateException("Can't populate result!", e);
+        }
+    }
+
+    @Override
+    public void reset() {
+        close();
+        rs = executeQuery();
+    }
+
+    @Override
+    public void close() {
+        try {
+            if (rs != null) {
+                rs.close();
+                rs = null;
+            }
+            if (conn != null) {
+                conn.close();
+                conn = null;
+            }
+        } catch (SQLException e) {
+            throw new IllegalStateException("Can't close ResultSet!", e);
+        }
+    }
+
+}

http://git-wip-us.apache.org/repos/asf/incubator-kylin/blob/4b631f92/query/src/main/java/com/kylinolap/query/enumerator/LookupTableEnumerator.java
----------------------------------------------------------------------
diff --git a/query/src/main/java/com/kylinolap/query/enumerator/LookupTableEnumerator.java b/query/src/main/java/com/kylinolap/query/enumerator/LookupTableEnumerator.java
new file mode 100644
index 0000000..e0079ea
--- /dev/null
+++ b/query/src/main/java/com/kylinolap/query/enumerator/LookupTableEnumerator.java
@@ -0,0 +1,96 @@
+/*
+ * Copyright 2013-2014 eBay Software Foundation
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.kylinolap.query.enumerator;
+
+import java.util.Collection;
+import java.util.Iterator;
+import java.util.List;
+
+import net.hydromatic.linq4j.Enumerator;
+
+import com.kylinolap.cube.CubeInstance;
+import com.kylinolap.cube.CubeManager;
+import com.kylinolap.dict.lookup.LookupStringTable;
+import com.kylinolap.metadata.model.cube.DimensionDesc;
+import com.kylinolap.metadata.model.schema.ColumnDesc;
+import com.kylinolap.query.relnode.OLAPContext;
+import com.kylinolap.query.schema.OLAPTable;
+import com.kylinolap.storage.tuple.Tuple;
+
+/**
+ * @author yangli9
+ * 
+ */
+public class LookupTableEnumerator implements Enumerator<Object[]> {
+
+    private final Collection<String[]> allRows;
+    private final List<ColumnDesc> colDescs;
+    private final Object[] current;
+    private Iterator<String[]> iterator;
+
+    public LookupTableEnumerator(OLAPContext olapContext) {
+
+        String lookupTableName = olapContext.firstTableScan.getCubeTable();
+        DimensionDesc dim = olapContext.cubeDesc.findDimensionByTable(lookupTableName);
+        if (dim == null)
+            throw new IllegalStateException("No dimension with derived columns found for lookup table " + lookupTableName + ", cube desc " + olapContext.cubeDesc);
+
+        CubeInstance cube = olapContext.cubeInstance;
+        CubeManager cubeMgr = CubeManager.getInstance(cube.getConfig());
+        LookupStringTable table = cubeMgr.getLookupTable(cube.getLatestReadySegment(), dim);
+        this.allRows = table.getAllRows();
+
+        OLAPTable olapTable = (OLAPTable) olapContext.firstTableScan.getOlapTable();
+        this.colDescs = olapTable.getExposedColumns();
+        this.current = new Object[colDescs.size()];
+
+        reset();
+    }
+
+    @Override
+    public boolean moveNext() {
+        boolean hasNext = iterator.hasNext();
+        if (hasNext) {
+            String[] row = iterator.next();
+            for (int i = 0, n = colDescs.size(); i < n; i++) {
+                ColumnDesc colDesc = colDescs.get(i);
+                int colIdx = colDesc.getZeroBasedIndex();
+                if (colIdx >= 0) {
+                    current[i] = Tuple.convertOptiqCellValue(row[colIdx], colDesc.getType().getName());
+                } else {
+                    current[i] = null; // fake column
+                }
+            }
+        }
+        return hasNext;
+    }
+
+    @Override
+    public Object[] current() {
+        return current;
+    }
+
+    @Override
+    public void reset() {
+        this.iterator = allRows.iterator();
+    }
+
+    @Override
+    public void close() {
+    }
+
+}

http://git-wip-us.apache.org/repos/asf/incubator-kylin/blob/4b631f92/query/src/main/java/com/kylinolap/query/enumerator/OLAPQuery.java
----------------------------------------------------------------------
diff --git a/query/src/main/java/com/kylinolap/query/enumerator/OLAPQuery.java b/query/src/main/java/com/kylinolap/query/enumerator/OLAPQuery.java
new file mode 100644
index 0000000..78a5cf1
--- /dev/null
+++ b/query/src/main/java/com/kylinolap/query/enumerator/OLAPQuery.java
@@ -0,0 +1,65 @@
+/*
+ * Copyright 2013-2014 eBay Software Foundation
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.kylinolap.query.enumerator;
+
+import net.hydromatic.linq4j.AbstractEnumerable;
+import net.hydromatic.linq4j.Enumerable;
+import net.hydromatic.linq4j.Enumerator;
+import net.hydromatic.optiq.DataContext;
+
+import com.kylinolap.query.relnode.OLAPContext;
+
+/**
+ * 
+ * @author xjiang
+ * 
+ */
+public class OLAPQuery extends AbstractEnumerable<Object[]> implements Enumerable<Object[]> {
+
+    public static final String PROP_SCAN_THRESHOLD = "scan_threshold";
+
+    public enum EnumeratorTypeEnum {
+        CUBE, LOOKUP_TABLE, HIVE
+    }
+
+    private final DataContext optiqContext;
+    private final EnumeratorTypeEnum type;
+    private final int contextId;
+
+    public OLAPQuery(DataContext optiqContext, EnumeratorTypeEnum type, int ctxId) {
+        this.optiqContext = optiqContext;
+        this.type = type;
+        this.contextId = ctxId;
+    }
+
+    public OLAPQuery(EnumeratorTypeEnum type, int ctxSeq) {
+        this(null, type, ctxSeq);
+    }
+
+    public Enumerator<Object[]> enumerator() {
+        OLAPContext olapContext = OLAPContext.getThreadLocalContextById(contextId);
+        switch (type) {
+        case CUBE:
+            return new CubeEnumerator(olapContext, optiqContext);
+        case LOOKUP_TABLE:
+            return new LookupTableEnumerator(olapContext);
+        case HIVE:
+            return new HiveEnumerator(olapContext);
+        default:
+            throw new IllegalArgumentException("Wrong type " + type + "!");
+        }
+    }
+}

http://git-wip-us.apache.org/repos/asf/incubator-kylin/blob/4b631f92/query/src/main/java/com/kylinolap/query/optrule/OLAPAggregateRule.java
----------------------------------------------------------------------
diff --git a/query/src/main/java/com/kylinolap/query/optrule/OLAPAggregateRule.java b/query/src/main/java/com/kylinolap/query/optrule/OLAPAggregateRule.java
new file mode 100644
index 0000000..c1c5603
--- /dev/null
+++ b/query/src/main/java/com/kylinolap/query/optrule/OLAPAggregateRule.java
@@ -0,0 +1,52 @@
+/*
+ * Copyright 2013-2014 eBay Software Foundation
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.kylinolap.query.optrule;
+
+import org.eigenbase.rel.AggregateRel;
+import org.eigenbase.rel.InvalidRelException;
+import org.eigenbase.rel.RelNode;
+import org.eigenbase.rel.convert.ConverterRule;
+import org.eigenbase.relopt.Convention;
+import org.eigenbase.relopt.RelTraitSet;
+
+import com.kylinolap.query.relnode.OLAPAggregateRel;
+import com.kylinolap.query.relnode.OLAPRel;
+
+/**
+ * 
+ * @author xjiang
+ * 
+ */
+public class OLAPAggregateRule extends ConverterRule {
+
+    public static final ConverterRule INSTANCE = new OLAPAggregateRule();
+
+    public OLAPAggregateRule() {
+        super(AggregateRel.class, Convention.NONE, OLAPRel.CONVENTION, "OLAPAggregateRule");
+    }
+
+    @Override
+    public RelNode convert(RelNode rel) {
+        AggregateRel agg = (AggregateRel) rel;
+        RelTraitSet traitSet = agg.getTraitSet().replace(OLAPRel.CONVENTION);
+        try {
+            return new OLAPAggregateRel(agg.getCluster(), traitSet, convert(agg.getChild(), traitSet), agg.getGroupSet(), agg.getAggCallList());
+        } catch (InvalidRelException e) {
+            throw new IllegalStateException("Can't create OLAPAggregateRel!", e);
+        }
+    }
+
+}

http://git-wip-us.apache.org/repos/asf/incubator-kylin/blob/4b631f92/query/src/main/java/com/kylinolap/query/optrule/OLAPFilterRule.java
----------------------------------------------------------------------
diff --git a/query/src/main/java/com/kylinolap/query/optrule/OLAPFilterRule.java b/query/src/main/java/com/kylinolap/query/optrule/OLAPFilterRule.java
new file mode 100644
index 0000000..4c35616
--- /dev/null
+++ b/query/src/main/java/com/kylinolap/query/optrule/OLAPFilterRule.java
@@ -0,0 +1,49 @@
+/*
+ * Copyright 2013-2014 eBay Software Foundation
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.kylinolap.query.optrule;
+
+import org.eigenbase.rel.FilterRel;
+import org.eigenbase.relopt.RelOptRule;
+import org.eigenbase.relopt.RelOptRuleCall;
+import org.eigenbase.relopt.RelTraitSet;
+
+import com.kylinolap.query.relnode.OLAPFilterRel;
+import com.kylinolap.query.relnode.OLAPRel;
+
+/**
+ * 
+ * @author xjiang
+ * 
+ */
+
+public class OLAPFilterRule extends RelOptRule {
+
+    public static final RelOptRule INSTANCE = new OLAPFilterRule();
+
+    public OLAPFilterRule() {
+        super(operand(FilterRel.class, any()));
+    }
+
+    @Override
+    public void onMatch(RelOptRuleCall call) {
+        FilterRel filter = call.rel(0);
+
+        RelTraitSet traitSet = filter.getTraitSet().replace(OLAPRel.CONVENTION);
+        OLAPFilterRel olapFilter = new OLAPFilterRel(filter.getCluster(), traitSet, convert(filter.getChild(), traitSet), filter.getCondition());
+        call.transformTo(olapFilter);
+    }
+
+}

http://git-wip-us.apache.org/repos/asf/incubator-kylin/blob/4b631f92/query/src/main/java/com/kylinolap/query/optrule/OLAPJoinRule.java
----------------------------------------------------------------------
diff --git a/query/src/main/java/com/kylinolap/query/optrule/OLAPJoinRule.java b/query/src/main/java/com/kylinolap/query/optrule/OLAPJoinRule.java
new file mode 100644
index 0000000..f3ac3df
--- /dev/null
+++ b/query/src/main/java/com/kylinolap/query/optrule/OLAPJoinRule.java
@@ -0,0 +1,81 @@
+/*
+ * Copyright 2013-2014 eBay Software Foundation
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.kylinolap.query.optrule;
+
+import org.eigenbase.rel.InvalidRelException;
+import org.eigenbase.rel.JoinInfo;
+import org.eigenbase.rel.JoinRel;
+import org.eigenbase.rel.JoinRelType;
+import org.eigenbase.rel.RelNode;
+import org.eigenbase.rel.convert.ConverterRule;
+import org.eigenbase.relopt.Convention;
+import org.eigenbase.relopt.RelOptCluster;
+import org.eigenbase.relopt.RelTraitSet;
+
+import com.kylinolap.query.relnode.OLAPFilterRel;
+import com.kylinolap.query.relnode.OLAPJoinRel;
+import com.kylinolap.query.relnode.OLAPRel;
+
+/**
+ * 
+ * @author xjiang
+ * 
+ */
+public class OLAPJoinRule extends ConverterRule {
+
+    public static final ConverterRule INSTANCE = new OLAPJoinRule();
+
+    public OLAPJoinRule() {
+        super(JoinRel.class, Convention.NONE, OLAPRel.CONVENTION, "OLAPJoinRule");
+    }
+
+    @Override
+    public RelNode convert(RelNode rel) {
+        JoinRel join = (JoinRel) rel;
+        RelNode left = join.getInput(0);
+        RelNode right = join.getInput(1);
+
+        RelTraitSet traitSet = join.getTraitSet().replace(OLAPRel.CONVENTION);
+        left = convert(left, traitSet);
+        right = convert(right, traitSet);
+
+        final JoinInfo info = JoinInfo.of(left, right, join.getCondition());
+        if (!info.isEqui() && join.getJoinType() != JoinRelType.INNER) {
+            // EnumerableJoinRel only supports equi-join. We can put a filter on top
+            // if it is an inner join.
+            return null;
+        }
+
+        RelOptCluster cluster = join.getCluster();
+        RelNode newRel;
+        try {
+            newRel = new OLAPJoinRel(cluster, traitSet, left, right, //
+                    info.getEquiCondition(left, right, cluster.getRexBuilder()), //
+                    info.leftKeys, info.rightKeys, join.getJoinType(), join.getVariablesStopped());
+        } catch (InvalidRelException e) {
+            // Semantic error not possible. Must be a bug. Convert to
+            // internal error.
+            throw new AssertionError(e);
+            // LOGGER.fine(e.toString());
+            // return null;
+        }
+        if (!info.isEqui()) {
+            newRel = new OLAPFilterRel(cluster, newRel.getTraitSet(), newRel, info.getRemaining(cluster.getRexBuilder()));
+        }
+        return newRel;
+    }
+
+}

http://git-wip-us.apache.org/repos/asf/incubator-kylin/blob/4b631f92/query/src/main/java/com/kylinolap/query/optrule/OLAPLimitRule.java
----------------------------------------------------------------------
diff --git a/query/src/main/java/com/kylinolap/query/optrule/OLAPLimitRule.java b/query/src/main/java/com/kylinolap/query/optrule/OLAPLimitRule.java
new file mode 100644
index 0000000..af14759
--- /dev/null
+++ b/query/src/main/java/com/kylinolap/query/optrule/OLAPLimitRule.java
@@ -0,0 +1,56 @@
+/*
+ * Copyright 2013-2014 eBay Software Foundation
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.kylinolap.query.optrule;
+
+import org.eigenbase.rel.RelNode;
+import org.eigenbase.rel.SortRel;
+import org.eigenbase.relopt.RelOptRule;
+import org.eigenbase.relopt.RelOptRuleCall;
+import org.eigenbase.relopt.RelTraitSet;
+
+import com.kylinolap.query.relnode.OLAPLimitRel;
+import com.kylinolap.query.relnode.OLAPRel;
+
+/**
+ * 
+ * @author xjiang
+ * 
+ */
+public class OLAPLimitRule extends RelOptRule {
+
+    public static final RelOptRule INSTANCE = new OLAPLimitRule();
+
+    public OLAPLimitRule() {
+        super(operand(SortRel.class, any()), "OLAPLimitRule");
+    }
+
+    @Override
+    public void onMatch(RelOptRuleCall call) {
+        final SortRel sort = call.rel(0);
+        if (sort.offset == null && sort.fetch == null) {
+            return;
+        }
+        final RelTraitSet traitSet = sort.getTraitSet().replace(OLAPRel.CONVENTION);
+        RelNode input = sort.getChild();
+        if (!sort.getCollation().getFieldCollations().isEmpty()) {
+            // Create a sort with the same sort key, but no offset or fetch.
+            input = sort.copy(sort.getTraitSet(), input, sort.getCollation(), null, null);
+        }
+        RelNode x = convert(input, input.getTraitSet().replace(OLAPRel.CONVENTION));
+        call.transformTo(new OLAPLimitRel(sort.getCluster(), traitSet, x, sort.offset, sort.fetch));
+    }
+
+}

http://git-wip-us.apache.org/repos/asf/incubator-kylin/blob/4b631f92/query/src/main/java/com/kylinolap/query/optrule/OLAPProjectRule.java
----------------------------------------------------------------------
diff --git a/query/src/main/java/com/kylinolap/query/optrule/OLAPProjectRule.java b/query/src/main/java/com/kylinolap/query/optrule/OLAPProjectRule.java
new file mode 100644
index 0000000..e174366
--- /dev/null
+++ b/query/src/main/java/com/kylinolap/query/optrule/OLAPProjectRule.java
@@ -0,0 +1,48 @@
+/*
+ * Copyright 2013-2014 eBay Software Foundation
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.kylinolap.query.optrule;
+
+import org.eigenbase.rel.ProjectRel;
+import org.eigenbase.relopt.RelOptRule;
+import org.eigenbase.relopt.RelOptRuleCall;
+import org.eigenbase.relopt.RelTraitSet;
+
+import com.kylinolap.query.relnode.OLAPProjectRel;
+import com.kylinolap.query.relnode.OLAPRel;
+
+/**
+ * 
+ * @author xjiang
+ * 
+ */
+public class OLAPProjectRule extends RelOptRule {
+
+    public static final RelOptRule INSTANCE = new OLAPProjectRule();
+
+    public OLAPProjectRule() {
+        super(operand(ProjectRel.class, any()));
+    }
+
+    @Override
+    public void onMatch(RelOptRuleCall call) {
+        ProjectRel project = call.rel(0);
+
+        RelTraitSet traitSet = project.getTraitSet().replace(OLAPRel.CONVENTION);
+        OLAPProjectRel olapProj = new OLAPProjectRel(project.getCluster(), traitSet, convert(project.getChild(), project.getTraitSet().replace(OLAPRel.CONVENTION)), project.getProjects(), project.getRowType(), project.getFlags());
+        call.transformTo(olapProj);
+    }
+
+}

http://git-wip-us.apache.org/repos/asf/incubator-kylin/blob/4b631f92/query/src/main/java/com/kylinolap/query/optrule/OLAPSortRule.java
----------------------------------------------------------------------
diff --git a/query/src/main/java/com/kylinolap/query/optrule/OLAPSortRule.java b/query/src/main/java/com/kylinolap/query/optrule/OLAPSortRule.java
new file mode 100644
index 0000000..cccfe47
--- /dev/null
+++ b/query/src/main/java/com/kylinolap/query/optrule/OLAPSortRule.java
@@ -0,0 +1,51 @@
+/*
+ * Copyright 2013-2014 eBay Software Foundation
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.kylinolap.query.optrule;
+
+import org.eigenbase.rel.RelNode;
+import org.eigenbase.rel.SortRel;
+import org.eigenbase.rel.convert.ConverterRule;
+import org.eigenbase.relopt.Convention;
+import org.eigenbase.relopt.RelTraitSet;
+
+import com.kylinolap.query.relnode.OLAPRel;
+import com.kylinolap.query.relnode.OLAPSortRel;
+
+/**
+ * @author xjiang
+ * 
+ */
+public class OLAPSortRule extends ConverterRule {
+
+    public static final OLAPSortRule INSTANCE = new OLAPSortRule();
+
+    public OLAPSortRule() {
+        super(SortRel.class, Convention.NONE, OLAPRel.CONVENTION, "OLAPSortRule");
+    }
+
+    @Override
+    public RelNode convert(RelNode rel) {
+        final SortRel sort = (SortRel) rel;
+        if (sort.offset != null || sort.fetch != null) {
+            return null;
+        }
+        final RelTraitSet traitSet = sort.getTraitSet().replace(OLAPRel.CONVENTION);
+        final RelNode input = sort.getChild();
+        return new OLAPSortRel(rel.getCluster(), traitSet, convert(input, input.getTraitSet().replace(OLAPRel.CONVENTION)), sort.getCollation(), sort.offset, sort.fetch);
+    }
+
+}

http://git-wip-us.apache.org/repos/asf/incubator-kylin/blob/4b631f92/query/src/main/java/com/kylinolap/query/optrule/OLAPToEnumerableConverterRule.java
----------------------------------------------------------------------
diff --git a/query/src/main/java/com/kylinolap/query/optrule/OLAPToEnumerableConverterRule.java b/query/src/main/java/com/kylinolap/query/optrule/OLAPToEnumerableConverterRule.java
new file mode 100644
index 0000000..ec11756
--- /dev/null
+++ b/query/src/main/java/com/kylinolap/query/optrule/OLAPToEnumerableConverterRule.java
@@ -0,0 +1,46 @@
+/*
+ * Copyright 2013-2014 eBay Software Foundation
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.kylinolap.query.optrule;
+
+import net.hydromatic.optiq.rules.java.EnumerableConvention;
+
+import org.eigenbase.rel.RelNode;
+import org.eigenbase.rel.convert.ConverterRule;
+import org.eigenbase.relopt.RelTraitSet;
+
+import com.kylinolap.query.relnode.OLAPRel;
+import com.kylinolap.query.relnode.OLAPToEnumerableConverter;
+
+/**
+ * 
+ * @author xjiang
+ * 
+ */
+public class OLAPToEnumerableConverterRule extends ConverterRule {
+
+    public static final ConverterRule INSTANCE = new OLAPToEnumerableConverterRule();
+
+    public OLAPToEnumerableConverterRule() {
+        super(RelNode.class, OLAPRel.CONVENTION, EnumerableConvention.INSTANCE, "OLAPToEnumerableConverterRule");
+    }
+
+    @Override
+    public RelNode convert(RelNode rel) {
+        RelTraitSet newTraitSet = rel.getTraitSet().replace(getOutConvention());
+        return new OLAPToEnumerableConverter(rel.getCluster(), newTraitSet, rel);
+    }
+
+}

http://git-wip-us.apache.org/repos/asf/incubator-kylin/blob/4b631f92/query/src/main/java/com/kylinolap/query/relnode/ColumnRowType.java
----------------------------------------------------------------------
diff --git a/query/src/main/java/com/kylinolap/query/relnode/ColumnRowType.java b/query/src/main/java/com/kylinolap/query/relnode/ColumnRowType.java
new file mode 100644
index 0000000..a0dd3cf
--- /dev/null
+++ b/query/src/main/java/com/kylinolap/query/relnode/ColumnRowType.java
@@ -0,0 +1,84 @@
+/*
+ * Copyright 2013-2014 eBay Software Foundation
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.kylinolap.query.relnode;
+
+import java.util.Collections;
+import java.util.List;
+import java.util.Set;
+
+import com.kylinolap.metadata.model.cube.TblColRef;
+
+/**
+ * 
+ * @author xjiang
+ * 
+ */
+public class ColumnRowType {
+
+    private List<TblColRef> columns;
+    // for calculated column, like (CASE LSTG_FORMAT_NAME WHEN 'Auction' THEN
+    // '111' ELSE '222' END)
+    // source columns are the contributing physical columns, here the
+    // LSTG_FORMAT_NAME
+    private List<Set<TblColRef>> sourceColumns;
+
+    public ColumnRowType(List<TblColRef> columns) {
+        this(columns, null);
+    }
+
+    public ColumnRowType(List<TblColRef> columns, List<Set<TblColRef>> sourceColumns) {
+        this.columns = columns;
+        this.sourceColumns = sourceColumns;
+    }
+
+    public TblColRef getColumnByIndex(int index) {
+        return columns.get(index);
+    }
+
+    public int getIndexByName(String columnName) {
+        for (int i = 0; i < columns.size(); i++) {
+            if (columns.get(i).getName().equals(columnName)) {
+                return i;
+            }
+        }
+        return -1;
+    }
+
+    public Set<TblColRef> getSourceColumnsByIndex(int i) {
+        Set<TblColRef> result = null;
+        if (sourceColumns != null) {
+            result = sourceColumns.get(i);
+        }
+        if (result == null || result.isEmpty()) {
+            result = Collections.singleton(getColumnByIndex(i));
+        }
+        return result;
+    }
+
+    public List<TblColRef> getAllColumns() {
+        return columns;
+    }
+
+    public int size() {
+        return columns.size();
+    }
+
+    @Override
+    public String toString() {
+        return "ColumnRowType [" + columns + "]";
+    }
+
+}

http://git-wip-us.apache.org/repos/asf/incubator-kylin/blob/4b631f92/query/src/main/java/com/kylinolap/query/relnode/OLAPAggregateRel.java
----------------------------------------------------------------------
diff --git a/query/src/main/java/com/kylinolap/query/relnode/OLAPAggregateRel.java b/query/src/main/java/com/kylinolap/query/relnode/OLAPAggregateRel.java
new file mode 100644
index 0000000..33a403d
--- /dev/null
+++ b/query/src/main/java/com/kylinolap/query/relnode/OLAPAggregateRel.java
@@ -0,0 +1,370 @@
+/*
+ * Copyright 2013-2014 eBay Software Foundation
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.kylinolap.query.relnode;
+
+import java.util.ArrayList;
+import java.util.BitSet;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
+
+import net.hydromatic.optiq.AggregateFunction;
+import net.hydromatic.optiq.FunctionParameter;
+import net.hydromatic.optiq.impl.AggregateFunctionImpl;
+import net.hydromatic.optiq.rules.java.EnumerableConvention;
+import net.hydromatic.optiq.rules.java.EnumerableRel;
+import net.hydromatic.optiq.rules.java.EnumerableRelImplementor;
+import net.hydromatic.optiq.rules.java.JavaRules.EnumerableAggregateRel;
+
+import org.eigenbase.rel.AggregateCall;
+import org.eigenbase.rel.AggregateRelBase;
+import org.eigenbase.rel.Aggregation;
+import org.eigenbase.rel.InvalidRelException;
+import org.eigenbase.rel.RelNode;
+import org.eigenbase.relopt.RelOptCluster;
+import org.eigenbase.relopt.RelOptCost;
+import org.eigenbase.relopt.RelOptPlanner;
+import org.eigenbase.relopt.RelTrait;
+import org.eigenbase.relopt.RelTraitSet;
+import org.eigenbase.reltype.RelDataType;
+import org.eigenbase.reltype.RelDataTypeFactory;
+import org.eigenbase.reltype.RelDataTypeField;
+import org.eigenbase.sql.SqlAggFunction;
+import org.eigenbase.sql.SqlIdentifier;
+import org.eigenbase.sql.fun.SqlSumEmptyIsZeroAggFunction;
+import org.eigenbase.sql.parser.SqlParserPos;
+import org.eigenbase.sql.type.InferTypes;
+import org.eigenbase.sql.type.OperandTypes;
+import org.eigenbase.sql.type.ReturnTypes;
+import org.eigenbase.sql.type.SqlTypeFamily;
+import org.eigenbase.sql.validate.SqlUserDefinedAggFunction;
+import org.eigenbase.util.Util;
+
+import com.google.common.base.Preconditions;
+import com.kylinolap.metadata.model.cube.FunctionDesc;
+import com.kylinolap.metadata.model.cube.ParameterDesc;
+import com.kylinolap.metadata.model.cube.TblColRef;
+import com.kylinolap.metadata.model.schema.ColumnDesc;
+import com.kylinolap.metadata.model.schema.TableDesc;
+import com.kylinolap.query.sqlfunc.HLLDistinctCountAggFunc;
+
+/**
+ * @author xjiang
+ */
+public class OLAPAggregateRel extends AggregateRelBase implements OLAPRel, EnumerableRel {
+
+    private final static Map<String, String> AGGR_FUNC_MAP = new HashMap<String, String>();
+
+    static {
+        AGGR_FUNC_MAP.put("SUM", "SUM");
+        AGGR_FUNC_MAP.put("$SUM0", "SUM");
+        AGGR_FUNC_MAP.put("COUNT", "COUNT");
+        AGGR_FUNC_MAP.put("COUNT_DISTINCT", "COUNT_DISTINCT");
+        AGGR_FUNC_MAP.put("HLL_COUNT", "COUNT_DISTINCT");
+        AGGR_FUNC_MAP.put("MAX", "MAX");
+        AGGR_FUNC_MAP.put("MIN", "MIN");
+    }
+
+    private static String getFuncName(AggregateCall aggCall) {
+        String aggName = aggCall.getAggregation().getName();
+        if (aggCall.isDistinct()) {
+            aggName = aggName + "_DISTINCT";
+        }
+        String funcName = AGGR_FUNC_MAP.get(aggName);
+        if (funcName == null) {
+            throw new IllegalStateException("Don't suppoprt aggregation " + aggName);
+        }
+        return funcName;
+    }
+
+    private OLAPContext context;
+    private ColumnRowType columnRowType;
+    private boolean afterAggregate;
+    private List<AggregateCall> rewriteAggCalls;
+    private List<TblColRef> groups;
+    private List<FunctionDesc> aggregations;
+
+    public OLAPAggregateRel(RelOptCluster cluster, RelTraitSet traits, RelNode child, BitSet groupSet, List<AggregateCall> aggCalls) throws InvalidRelException {
+        super(cluster, traits, child, groupSet, aggCalls);
+        Preconditions.checkArgument(getConvention() == OLAPRel.CONVENTION);
+        this.afterAggregate = false;
+        this.rewriteAggCalls = aggCalls;
+        this.rowType = getRowType();
+    }
+
+    @Override
+    public AggregateRelBase copy(RelTraitSet traitSet, RelNode input, BitSet groupSet, List<AggregateCall> aggCalls) {
+        try {
+            return new OLAPAggregateRel(getCluster(), traitSet, input, groupSet, aggCalls);
+        } catch (InvalidRelException e) {
+            throw new IllegalStateException("Can't create OLAPAggregateRel!", e);
+        }
+    }
+
+    @Override
+    public RelOptCost computeSelfCost(RelOptPlanner planner) {
+        double factor = .5;
+        for (AggregateCall aggCall : aggCalls) {
+            if ("$SUM0".equals(aggCall.getAggregation().getName())) {
+                factor = .2;
+            }
+        }
+        return super.computeSelfCost(planner).multiplyBy(factor);
+    }
+
+    @Override
+    public void implementOLAP(OLAPImplementor implementor) {
+
+        implementor.visitChild(getChild(), this);
+
+        this.context = implementor.getContext();
+        this.columnRowType = buildColumnRowType();
+        this.afterAggregate = this.context.afterAggregate;
+
+        // only translate the first aggregation
+        if (!this.afterAggregate) {
+            translateGroupBy();
+            fillbackOptimizedColumn();
+            translateAggregation();
+            this.context.afterAggregate = true;
+        } else {
+            for (AggregateCall aggCall : aggCalls) {
+                // check if supported by kylin
+                if (aggCall.isDistinct()) {
+                    throw new IllegalStateException("Distinct count is only allowed in innermost sub-query.");
+                }
+            }
+        }
+    }
+
+    private ColumnRowType buildColumnRowType() {
+        buildGroups();
+        buildAggregations();
+
+        ColumnRowType inputColumnRowType = ((OLAPRel) getChild()).getColumnRowType();
+        List<TblColRef> columns = new ArrayList<TblColRef>(this.rowType.getFieldCount());
+        columns.addAll(this.groups);
+
+        for (int i = 0; i < this.aggregations.size(); i++) {
+            FunctionDesc aggFunc = this.aggregations.get(i);
+            TblColRef aggCol = null;
+            if (aggFunc.needRewrite()) {
+                aggCol = buildRewriteColumn(aggFunc);
+            } else {
+                AggregateCall aggCall = this.rewriteAggCalls.get(i);
+                if (!aggCall.getArgList().isEmpty()) {
+                    int index = aggCall.getArgList().get(0);
+                    aggCol = inputColumnRowType.getColumnByIndex(index);
+                }
+            }
+            columns.add(aggCol);
+        }
+        return new ColumnRowType(columns);
+    }
+
+    private TblColRef buildRewriteColumn(FunctionDesc aggFunc) {
+        TblColRef colRef = null;
+        if (aggFunc.needRewrite()) {
+            ColumnDesc column = new ColumnDesc();
+            column.setName(aggFunc.getRewriteFieldName());
+            TableDesc table = this.context.firstTableScan.getOlapTable().getSourceTable();
+            column.setTable(table);
+            colRef = new TblColRef(column);
+        }
+        return colRef;
+    }
+
+    private void buildGroups() {
+        ColumnRowType inputColumnRowType = ((OLAPRel) getChild()).getColumnRowType();
+        this.groups = new ArrayList<TblColRef>();
+        for (int i = getGroupSet().nextSetBit(0); i >= 0; i = getGroupSet().nextSetBit(i + 1)) {
+            Set<TblColRef> columns = inputColumnRowType.getSourceColumnsByIndex(i);
+            this.groups.addAll(columns);
+        }
+    }
+
+    private void buildAggregations() {
+        ColumnRowType inputColumnRowType = ((OLAPRel) getChild()).getColumnRowType();
+        this.aggregations = new ArrayList<FunctionDesc>();
+        for (AggregateCall aggCall : this.rewriteAggCalls) {
+            ParameterDesc parameter = null;
+            if (!aggCall.getArgList().isEmpty()) {
+                int index = aggCall.getArgList().get(0);
+                TblColRef column = inputColumnRowType.getColumnByIndex(index);
+                if (!column.isInnerColumn()) {
+                    parameter = new ParameterDesc();
+                    parameter.setValue(column.getName());
+                    parameter.setType("column");
+                }
+            }
+            FunctionDesc aggFunc = new FunctionDesc();
+            String funcName = getFuncName(aggCall);
+            aggFunc.setExpression(funcName);
+            aggFunc.setParameter(parameter);
+            this.aggregations.add(aggFunc);
+        }
+    }
+
+    private void translateGroupBy() {
+        context.groupByColumns.addAll(this.groups);
+    }
+
+    private void translateAggregation() {
+        ColumnRowType inputColumnRowType = ((OLAPRel) getChild()).getColumnRowType();
+        for (int i = 0; i < this.aggregations.size(); i++) {
+            FunctionDesc aggFunc = this.aggregations.get(i);
+            context.aggregations.add(aggFunc);
+            if (aggFunc.needRewrite()) {
+                String rewriteFieldName = aggFunc.getRewriteFieldName();
+                context.rewriteFields.put(rewriteFieldName, null);
+
+                TblColRef column = buildRewriteColumn(aggFunc);
+                this.context.metricsColumns.add(column);
+            }
+            AggregateCall aggCall = this.rewriteAggCalls.get(i);
+            if (!aggCall.getArgList().isEmpty()) {
+                int index = aggCall.getArgList().get(0);
+                TblColRef column = inputColumnRowType.getColumnByIndex(index);
+                if (!column.isInnerColumn()) {
+                    this.context.metricsColumns.add(column);
+                }
+            }
+        }
+    }
+
+    private void fillbackOptimizedColumn() {
+        // some aggcall will be optimized out in sub-query (e.g. tableau
+        // generated sql)
+        // we need to fill them back
+        RelDataType inputAggRow = getChild().getRowType();
+        RelDataType outputAggRow = getRowType();
+        if (inputAggRow.getFieldCount() != outputAggRow.getFieldCount()) {
+            for (RelDataTypeField inputField : inputAggRow.getFieldList()) {
+                String inputFieldName = inputField.getName();
+                if (outputAggRow.getField(inputFieldName, true) == null) {
+                    TblColRef column = this.columnRowType.getColumnByIndex(inputField.getIndex());
+                    this.context.metricsColumns.add(column);
+                }
+            }
+        }
+    }
+
+    @Override
+    public void implementRewrite(RewriteImplementor implementor) {
+        implementor.visitChild(this, getChild());
+
+        // only rewrite the first aggregation
+        if (!this.afterAggregate && RewriteImplementor.needRewrite(this.context)) {
+            // rewrite the aggCalls
+            this.rewriteAggCalls = new ArrayList<AggregateCall>(aggCalls.size());
+            for (int i = 0; i < this.aggCalls.size(); i++) {
+                AggregateCall aggCall = this.aggCalls.get(i);
+                FunctionDesc cubeFunc = this.context.aggregations.get(i);
+                if (cubeFunc.needRewrite()) {
+                    aggCall = rewriteAggregateCall(aggCall, cubeFunc);
+                }
+                this.rewriteAggCalls.add(aggCall);
+            }
+        }
+
+        // rebuild rowType & columnRowType
+        this.rowType = this.deriveRowType();
+        this.columnRowType = this.buildColumnRowType();
+
+    }
+
+    private AggregateCall rewriteAggregateCall(AggregateCall aggCall, FunctionDesc func) {
+
+        // rebuild parameters
+        List<Integer> newArgList = new ArrayList<Integer>(1);
+        String fieldName = func.getRewriteFieldName();
+        RelDataTypeField field = getChild().getRowType().getField(fieldName, true);
+        newArgList.add(field.getIndex());
+
+        // rebuild function
+        RelDataType fieldType = aggCall.getType();
+        Aggregation newAgg = aggCall.getAggregation();
+        if (func.isCountDistinct()) {
+            newAgg = createHyperLogLogAggFunction(fieldType);
+        } else if (func.isCount()) {
+            newAgg = new SqlSumEmptyIsZeroAggFunction(fieldType);
+        }
+
+        // rebuild aggregate call
+        AggregateCall newAggCall = new AggregateCall(newAgg, false, newArgList, fieldType, newAgg.getName());
+
+        // To make sure specified type matches the inferReturnType, or otherwise
+        // there will be assertion failure in optiq
+        // The problem is BIGINT != BIGINT NOT NULL
+        // Details see https://github.scm.corp.ebay.com/Kylin/Kylin/issues/323
+        SqlAggFunction aggFunction = (SqlAggFunction) newAggCall.getAggregation();
+        AggCallBinding callBinding = newAggCall.createBinding(this);
+        RelDataType inferReturnType = aggFunction.inferReturnType(callBinding);
+
+        return new AggregateCall(newAgg, false, newArgList, inferReturnType, newAgg.getName());
+    }
+
+    private Aggregation createHyperLogLogAggFunction(RelDataType returnType) {
+        RelDataTypeFactory typeFactory = getCluster().getTypeFactory();
+        SqlIdentifier sqlIdentifier = new SqlIdentifier("HLL_COUNT", new SqlParserPos(1, 1));
+        AggregateFunction aggFunction = AggregateFunctionImpl.create(HLLDistinctCountAggFunc.class);
+        List<RelDataType> argTypes = new ArrayList<RelDataType>();
+        List<SqlTypeFamily> typeFamilies = new ArrayList<SqlTypeFamily>();
+        for (FunctionParameter o : aggFunction.getParameters()) {
+            final RelDataType type = o.getType(typeFactory);
+            argTypes.add(type);
+            typeFamilies.add(Util.first(type.getSqlTypeName().getFamily(), SqlTypeFamily.ANY));
+        }
+        return new SqlUserDefinedAggFunction(sqlIdentifier, ReturnTypes.explicit(returnType), InferTypes.explicit(argTypes), OperandTypes.family(typeFamilies), aggFunction);
+    }
+
+    @Override
+    public Result implement(EnumerableRelImplementor implementor, Prefer pref) {
+
+        EnumerableAggregateRel enumAggRel;
+        try {
+            enumAggRel = new EnumerableAggregateRel(getCluster(), getCluster().traitSetOf(EnumerableConvention.INSTANCE), getChild(), this.groupSet, rewriteAggCalls);
+        } catch (InvalidRelException e) {
+            throw new IllegalStateException("Can't create EnumerableAggregateRel!", e);
+        }
+
+        return enumAggRel.implement(implementor, pref);
+    }
+
+    @Override
+    public OLAPContext getContext() {
+        return context;
+    }
+
+    @Override
+    public ColumnRowType getColumnRowType() {
+        return columnRowType;
+    }
+
+    @Override
+    public boolean hasSubQuery() {
+        OLAPRel olapChild = (OLAPRel) getChild();
+        return olapChild.hasSubQuery();
+    }
+
+    @Override
+    public RelTraitSet replaceTraitSet(RelTrait trait) {
+        RelTraitSet oldTraitSet = this.traitSet;
+        this.traitSet = this.traitSet.replace(trait);
+        return oldTraitSet;
+    }
+}

http://git-wip-us.apache.org/repos/asf/incubator-kylin/blob/4b631f92/query/src/main/java/com/kylinolap/query/relnode/OLAPContext.java
----------------------------------------------------------------------
diff --git a/query/src/main/java/com/kylinolap/query/relnode/OLAPContext.java b/query/src/main/java/com/kylinolap/query/relnode/OLAPContext.java
new file mode 100644
index 0000000..7de1817
--- /dev/null
+++ b/query/src/main/java/com/kylinolap/query/relnode/OLAPContext.java
@@ -0,0 +1,122 @@
+/*
+ * Copyright 2013-2014 eBay Software Foundation
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.kylinolap.query.relnode;
+
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.LinkedList;
+import java.util.List;
+import java.util.Map;
+
+import org.eigenbase.reltype.RelDataType;
+
+import com.kylinolap.cube.CubeInstance;
+import com.kylinolap.metadata.model.cube.CubeDesc;
+import com.kylinolap.metadata.model.cube.FunctionDesc;
+import com.kylinolap.metadata.model.cube.JoinDesc;
+import com.kylinolap.metadata.model.cube.TblColRef;
+import com.kylinolap.query.schema.OLAPSchema;
+import com.kylinolap.storage.StorageContext;
+import com.kylinolap.storage.filter.TupleFilter;
+
+/**
+ * @author xjiang
+ * 
+ */
+public class OLAPContext {
+
+    public static final String PRM_ACCEPT_PARTIAL_RESULT = "AcceptPartialResult";
+
+    private static final ThreadLocal<Map<String, String>> _localPrarameters = new ThreadLocal<Map<String, String>>();
+
+    private static final ThreadLocal<Map<Integer, OLAPContext>> _localContexts = new ThreadLocal<Map<Integer, OLAPContext>>();
+
+    public static void setParameters(Map<String, String> parameters) {
+        _localPrarameters.set(parameters);
+    }
+
+    public static void clearParameter() {
+        _localPrarameters.remove();
+    }
+
+    public static void registerContext(OLAPContext ctx) {
+        if (_localContexts.get() == null) {
+            Map<Integer, OLAPContext> contextMap = new HashMap<Integer, OLAPContext>();
+            _localContexts.set(contextMap);
+        }
+        _localContexts.get().put(ctx.id, ctx);
+    }
+
+    public static Collection<OLAPContext> getThreadLocalContexts() {
+        Map<Integer, OLAPContext> map = _localContexts.get();
+        return map == null ? null : map.values();
+    }
+
+    public static OLAPContext getThreadLocalContextById(int id) {
+        return _localContexts.get().get(id);
+    }
+
+    public static void clearThreadLocalContexts() {
+        _localContexts.remove();
+    }
+
+    public OLAPContext(int seq) {
+        this.id = seq;
+        this.storageContext = new StorageContext();
+        Map<String, String> parameters = _localPrarameters.get();
+        if (parameters != null) {
+            String acceptPartialResult = parameters.get(PRM_ACCEPT_PARTIAL_RESULT);
+            if (acceptPartialResult != null) {
+                this.storageContext.setAcceptPartialResult(Boolean.parseBoolean(acceptPartialResult));
+            }
+        }
+    }
+
+    public final int id;
+    public final StorageContext storageContext;
+
+    // query info
+    public OLAPSchema olapSchema = null;
+    public OLAPTableScan firstTableScan = null; // to be fact table scan except
+                                                // "select * from lookupTable"
+    public RelDataType olapRowType = null;
+    public boolean afterAggregate = false;
+    public boolean afterJoin = false;
+    public boolean hasJoin = false;
+
+    // cube metadata
+    public CubeInstance cubeInstance;
+    public CubeDesc cubeDesc;
+    public Collection<TblColRef> allColumns = new HashSet<TblColRef>();
+    public Collection<TblColRef> metricsColumns = new HashSet<TblColRef>();
+    public Collection<TblColRef> groupByColumns = new ArrayList<TblColRef>();
+    public List<FunctionDesc> aggregations = new ArrayList<FunctionDesc>();
+    public List<JoinDesc> joins = new LinkedList<JoinDesc>();
+    public TupleFilter filter;
+
+    // rewrite info
+    public Map<String, RelDataType> rewriteFields = new HashMap<String, RelDataType>();
+
+    // hive query
+    public String sql = "";
+
+    public boolean isSimpleQuery() {
+        return (joins.size() == 0) && (groupByColumns.size() == 0) && (aggregations.size() == 0);
+    }
+}

http://git-wip-us.apache.org/repos/asf/incubator-kylin/blob/4b631f92/query/src/main/java/com/kylinolap/query/relnode/OLAPFilterRel.java
----------------------------------------------------------------------
diff --git a/query/src/main/java/com/kylinolap/query/relnode/OLAPFilterRel.java b/query/src/main/java/com/kylinolap/query/relnode/OLAPFilterRel.java
new file mode 100644
index 0000000..923a540
--- /dev/null
+++ b/query/src/main/java/com/kylinolap/query/relnode/OLAPFilterRel.java
@@ -0,0 +1,326 @@
+/*
+ * Copyright 2013-2014 eBay Software Foundation
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.kylinolap.query.relnode;
+
+import java.util.Calendar;
+import java.util.GregorianCalendar;
+import java.util.LinkedList;
+import java.util.List;
+
+import net.hydromatic.optiq.rules.java.EnumerableConvention;
+import net.hydromatic.optiq.rules.java.EnumerableRel;
+import net.hydromatic.optiq.rules.java.EnumerableRelImplementor;
+import net.hydromatic.optiq.rules.java.JavaRules.EnumerableCalcRel;
+import net.hydromatic.optiq.runtime.SqlFunctions;
+
+import org.eigenbase.rel.FilterRelBase;
+import org.eigenbase.rel.RelCollation;
+import org.eigenbase.rel.RelNode;
+import org.eigenbase.relopt.RelOptCluster;
+import org.eigenbase.relopt.RelOptCost;
+import org.eigenbase.relopt.RelOptPlanner;
+import org.eigenbase.relopt.RelTrait;
+import org.eigenbase.relopt.RelTraitSet;
+import org.eigenbase.reltype.RelDataType;
+import org.eigenbase.rex.RexBuilder;
+import org.eigenbase.rex.RexCall;
+import org.eigenbase.rex.RexDynamicParam;
+import org.eigenbase.rex.RexInputRef;
+import org.eigenbase.rex.RexLiteral;
+import org.eigenbase.rex.RexLocalRef;
+import org.eigenbase.rex.RexNode;
+import org.eigenbase.rex.RexProgram;
+import org.eigenbase.rex.RexProgramBuilder;
+import org.eigenbase.rex.RexVisitorImpl;
+import org.eigenbase.sql.SqlKind;
+import org.eigenbase.sql.SqlOperator;
+import org.eigenbase.util.NlsString;
+
+import com.google.common.base.Preconditions;
+import com.google.common.collect.ImmutableList;
+import com.kylinolap.metadata.model.cube.TblColRef;
+import com.kylinolap.storage.filter.CaseTupleFilter;
+import com.kylinolap.storage.filter.ColumnTupleFilter;
+import com.kylinolap.storage.filter.CompareTupleFilter;
+import com.kylinolap.storage.filter.ConstantTupleFilter;
+import com.kylinolap.storage.filter.DynamicTupleFilter;
+import com.kylinolap.storage.filter.ExtractTupleFilter;
+import com.kylinolap.storage.filter.LogicalTupleFilter;
+import com.kylinolap.storage.filter.TupleFilter;
+import com.kylinolap.storage.filter.TupleFilter.FilterOperatorEnum;
+
+/**
+ * @author xjiang
+ */
+public class OLAPFilterRel extends FilterRelBase implements OLAPRel, EnumerableRel {
+
+    private static class TupleFilterVisitor extends RexVisitorImpl<TupleFilter> {
+
+        private final ColumnRowType inputRowType;
+        private final OLAPContext context;
+
+        public TupleFilterVisitor(ColumnRowType inputRowType, OLAPContext context) {
+            super(true);
+            this.inputRowType = inputRowType;
+            this.context = context;
+        }
+
+        @Override
+        public TupleFilter visitCall(RexCall call) {
+            TupleFilter filter = null;
+            SqlOperator op = call.getOperator();
+            switch (op.getKind()) {
+            case AND:
+                filter = new LogicalTupleFilter(FilterOperatorEnum.AND);
+                break;
+            case OR:
+                filter = new LogicalTupleFilter(FilterOperatorEnum.OR);
+                break;
+            case NOT:
+                filter = new LogicalTupleFilter(FilterOperatorEnum.NOT);
+                break;
+            case EQUALS:
+                filter = new CompareTupleFilter(FilterOperatorEnum.EQ);
+                break;
+            case GREATER_THAN:
+                filter = new CompareTupleFilter(FilterOperatorEnum.GT);
+                break;
+            case LESS_THAN:
+                filter = new CompareTupleFilter(FilterOperatorEnum.LT);
+                break;
+            case GREATER_THAN_OR_EQUAL:
+                filter = new CompareTupleFilter(FilterOperatorEnum.GTE);
+                break;
+            case LESS_THAN_OR_EQUAL:
+                filter = new CompareTupleFilter(FilterOperatorEnum.LTE);
+                break;
+            case NOT_EQUALS:
+                filter = new CompareTupleFilter(FilterOperatorEnum.NEQ);
+                break;
+            case IS_NULL:
+                filter = new CompareTupleFilter(FilterOperatorEnum.ISNULL);
+                break;
+            case IS_NOT_NULL:
+                filter = new CompareTupleFilter(FilterOperatorEnum.ISNOTNULL);
+                break;
+            case CAST:
+            case REINTERPRET:
+                // NOTE: use child directly
+                break;
+            case CASE:
+                filter = new CaseTupleFilter();
+                break;
+            case OTHER:
+                if (op.getName().equalsIgnoreCase("extract_date")) {
+                    filter = new ExtractTupleFilter(FilterOperatorEnum.EXTRACT);
+                } else {
+                    throw new UnsupportedOperationException(op.getName());
+                }
+                break;
+            default:
+                throw new UnsupportedOperationException(op.getName());
+            }
+
+            for (RexNode operand : call.operands) {
+                TupleFilter childFilter = operand.accept(this);
+                if (filter == null) {
+                    filter = childFilter;
+                } else {
+                    filter.addChild(childFilter);
+                }
+            }
+
+            if (op.getKind() == SqlKind.OR) {
+                CompareTupleFilter inFilter = mergeToInClause(filter);
+                if (inFilter != null) {
+                    filter = inFilter;
+                }
+            }
+            return filter;
+        }
+
+        private CompareTupleFilter mergeToInClause(TupleFilter filter) {
+            List<? extends TupleFilter> children = filter.getChildren();
+            TblColRef inColumn = null;
+            List<String> inValues = new LinkedList<String>();
+            for (TupleFilter child : children) {
+                if (child.getOperator() == FilterOperatorEnum.EQ) {
+                    CompareTupleFilter compFilter = (CompareTupleFilter) child;
+                    TblColRef column = compFilter.getColumn();
+                    if (inColumn == null) {
+                        inColumn = column;
+                    }
+
+                    if (column == null || !column.equals(inColumn)) {
+                        return null;
+                    }
+                    inValues.addAll(compFilter.getValues());
+                } else {
+                    return null;
+                }
+            }
+
+            children.clear();
+
+            CompareTupleFilter inFilter = new CompareTupleFilter(FilterOperatorEnum.IN);
+            inFilter.addChild(new ColumnTupleFilter(inColumn));
+            inFilter.addChild(new ConstantTupleFilter(inValues));
+            return inFilter;
+        }
+
+        @Override
+        public TupleFilter visitLocalRef(RexLocalRef localRef) {
+            throw new UnsupportedOperationException("local ref:" + localRef);
+        }
+
+        @Override
+        public TupleFilter visitInputRef(RexInputRef inputRef) {
+            TblColRef column = inputRowType.getColumnByIndex(inputRef.getIndex());
+            context.allColumns.add(column);
+            ColumnTupleFilter filter = new ColumnTupleFilter(column);
+            return filter;
+        }
+
+        private String normToTwoDigits(int i) {
+            if (i < 10)
+                return "0" + i;
+            else
+                return "" + i;
+        }
+
+        @Override
+        public TupleFilter visitLiteral(RexLiteral literal) {
+            String strValue = null;
+            Object literalValue = literal.getValue();
+            if (literalValue instanceof NlsString) {
+                strValue = ((NlsString) literalValue).getValue();
+            } else if (literalValue instanceof GregorianCalendar) {
+                GregorianCalendar g = (GregorianCalendar) literalValue;
+                strValue = "" + g.get(Calendar.YEAR) + "-" + normToTwoDigits(g.get(Calendar.MONTH) + 1) + "-" + normToTwoDigits(g.get(Calendar.DAY_OF_MONTH));
+            } else if (literalValue instanceof SqlFunctions.TimeUnitRange) {
+                // Extract(x from y) in where clause
+                strValue = ((SqlFunctions.TimeUnitRange) literalValue).name();
+            } else if (literalValue == null) {
+                strValue = null;
+            } else {
+                strValue = literalValue.toString();
+            }
+            TupleFilter filter = new ConstantTupleFilter(strValue);
+            return filter;
+        }
+
+        @Override
+        public TupleFilter visitDynamicParam(RexDynamicParam dynamicParam) {
+            String name = dynamicParam.getName();
+            TupleFilter filter = new DynamicTupleFilter(name);
+            return filter;
+        }
+    }
+
+    private ColumnRowType columnRowType;
+    private OLAPContext context;
+
+    public OLAPFilterRel(RelOptCluster cluster, RelTraitSet traits, RelNode child, RexNode condition) {
+        super(cluster, traits, child, condition);
+        Preconditions.checkArgument(getConvention() == OLAPRel.CONVENTION);
+        Preconditions.checkArgument(getConvention() == child.getConvention());
+        this.rowType = getRowType();
+    }
+
+    @Override
+    public RelOptCost computeSelfCost(RelOptPlanner planner) {
+        return super.computeSelfCost(planner).multiplyBy(.05);
+    }
+
+    @Override
+    public FilterRelBase copy(RelTraitSet traitSet, RelNode input, RexNode condition) {
+        return new OLAPFilterRel(getCluster(), traitSet, input, condition);
+    }
+
+    @Override
+    public void implementOLAP(OLAPImplementor implementor) {
+        implementor.visitChild(getChild(), this);
+
+        this.columnRowType = buildColumnRowType();
+        this.context = implementor.getContext();
+
+        // only translate where clause and don't translate having clause
+        if (!context.afterAggregate) {
+            translateFilter(context);
+        }
+    }
+
+    private ColumnRowType buildColumnRowType() {
+        OLAPRel olapChild = (OLAPRel) getChild();
+        ColumnRowType inputColumnRowType = olapChild.getColumnRowType();
+        return inputColumnRowType;
+    }
+
+    private void translateFilter(OLAPContext context) {
+        if (this.condition == null) {
+            return;
+        }
+
+        TupleFilterVisitor visitor = new TupleFilterVisitor(this.columnRowType, context);
+        context.filter = this.condition.accept(visitor);
+    }
+
+    @Override
+    public Result implement(EnumerableRelImplementor implementor, Prefer pref) {
+        // keep it for having clause
+        RexBuilder rexBuilder = getCluster().getRexBuilder();
+        RelDataType inputRowType = getChild().getRowType();
+        RexProgramBuilder programBuilder = new RexProgramBuilder(inputRowType, rexBuilder);
+        programBuilder.addIdentity();
+        programBuilder.addCondition(this.condition);
+        RexProgram program = programBuilder.getProgram();
+
+        EnumerableCalcRel enumCalcRel = new EnumerableCalcRel(getCluster(), getCluster().traitSetOf(EnumerableConvention.INSTANCE), getChild(), this.rowType, program, ImmutableList.<RelCollation> of());
+
+        return enumCalcRel.implement(implementor, pref);
+    }
+
+    @Override
+    public void implementRewrite(RewriteImplementor implementor) {
+        implementor.visitChild(this, getChild());
+
+        this.rowType = this.deriveRowType();
+        this.columnRowType = buildColumnRowType();
+    }
+
+    @Override
+    public OLAPContext getContext() {
+        return context;
+    }
+
+    @Override
+    public ColumnRowType getColumnRowType() {
+        return columnRowType;
+    }
+
+    @Override
+    public boolean hasSubQuery() {
+        OLAPRel olapChild = (OLAPRel) getChild();
+        return olapChild.hasSubQuery();
+    }
+
+    @Override
+    public RelTraitSet replaceTraitSet(RelTrait trait) {
+        RelTraitSet oldTraitSet = this.traitSet;
+        this.traitSet = this.traitSet.replace(trait);
+        return oldTraitSet;
+    }
+}

http://git-wip-us.apache.org/repos/asf/incubator-kylin/blob/4b631f92/query/src/main/java/com/kylinolap/query/relnode/OLAPJoinRel.java
----------------------------------------------------------------------
diff --git a/query/src/main/java/com/kylinolap/query/relnode/OLAPJoinRel.java b/query/src/main/java/com/kylinolap/query/relnode/OLAPJoinRel.java
new file mode 100644
index 0000000..83e7993
--- /dev/null
+++ b/query/src/main/java/com/kylinolap/query/relnode/OLAPJoinRel.java
@@ -0,0 +1,294 @@
+/*
+ * Copyright 2013-2014 eBay Software Foundation
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.kylinolap.query.relnode;
+
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.LinkedList;
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
+
+import net.hydromatic.linq4j.expressions.Blocks;
+import net.hydromatic.linq4j.expressions.Expressions;
+import net.hydromatic.optiq.rules.java.EnumerableRelImplementor;
+import net.hydromatic.optiq.rules.java.JavaRules.EnumerableJoinRel;
+import net.hydromatic.optiq.rules.java.PhysType;
+import net.hydromatic.optiq.rules.java.PhysTypeImpl;
+
+import org.eigenbase.rel.InvalidRelException;
+import org.eigenbase.rel.JoinInfo;
+import org.eigenbase.rel.JoinRelType;
+import org.eigenbase.rel.RelNode;
+import org.eigenbase.relopt.RelOptCluster;
+import org.eigenbase.relopt.RelOptCost;
+import org.eigenbase.relopt.RelOptPlanner;
+import org.eigenbase.relopt.RelOptTable;
+import org.eigenbase.relopt.RelTrait;
+import org.eigenbase.relopt.RelTraitSet;
+import org.eigenbase.reltype.RelDataType;
+import org.eigenbase.reltype.RelDataTypeFactory.FieldInfoBuilder;
+import org.eigenbase.reltype.RelDataTypeField;
+import org.eigenbase.reltype.RelDataTypeFieldImpl;
+import org.eigenbase.rex.RexCall;
+import org.eigenbase.rex.RexInputRef;
+import org.eigenbase.rex.RexNode;
+import org.eigenbase.sql.SqlKind;
+import org.eigenbase.util.ImmutableIntList;
+
+import com.google.common.base.Preconditions;
+import com.kylinolap.metadata.model.cube.JoinDesc;
+import com.kylinolap.metadata.model.cube.TblColRef;
+import com.kylinolap.query.schema.OLAPTable;
+
+/**
+ * @author xjiang
+ */
+public class OLAPJoinRel extends EnumerableJoinRel implements OLAPRel {
+
+    private final static String[] COLUMN_ARRAY_MARKER = new String[0];
+
+    private OLAPContext context;
+    private ColumnRowType columnRowType;
+    private boolean isTopJoin;
+    private boolean hasSubQuery;
+
+    public OLAPJoinRel(RelOptCluster cluster, RelTraitSet traits, RelNode left, RelNode right, //
+            RexNode condition, ImmutableIntList leftKeys, ImmutableIntList rightKeys, //
+            JoinRelType joinType, Set<String> variablesStopped) throws InvalidRelException {
+        super(cluster, traits, left, right, condition, leftKeys, rightKeys, joinType, variablesStopped);
+        Preconditions.checkArgument(getConvention() == OLAPRel.CONVENTION);
+        this.rowType = getRowType();
+        this.isTopJoin = false;
+    }
+
+    @Override
+    public EnumerableJoinRel copy(RelTraitSet traitSet, RexNode conditionExpr, RelNode left, RelNode right, //
+            JoinRelType joinType, boolean semiJoinDone) {
+
+        final JoinInfo joinInfo = JoinInfo.of(left, right, condition);
+        assert joinInfo.isEqui();
+        try {
+            return new OLAPJoinRel(getCluster(), traitSet, left, right, condition, joinInfo.leftKeys, joinInfo.rightKeys, joinType, variablesStopped);
+        } catch (InvalidRelException e) {
+            // Semantic error not possible. Must be a bug. Convert to
+            // internal error.
+            throw new AssertionError(e);
+        }
+    }
+
+    @Override
+    public RelOptCost computeSelfCost(RelOptPlanner planner) {
+        return super.computeSelfCost(planner).multiplyBy(.05);
+    }
+
+    @Override
+    public double getRows() {
+        return super.getRows() * 0.1;
+    }
+
+    @Override
+    public void implementOLAP(OLAPImplementor implementor) {
+
+        // create context for root join
+        if (!(implementor.getParentNode() instanceof OLAPJoinRel)) {
+            implementor.allocateContext();
+        }
+        this.context = implementor.getContext();
+        this.isTopJoin = !this.context.hasJoin;
+        this.context.hasJoin = true;
+
+        // as we keep the first table as fact table, we need to visit from left
+        // to right
+        implementor.visitChild(this.left, this);
+        if (this.context != implementor.getContext() || ((OLAPRel) this.left).hasSubQuery()) {
+            this.hasSubQuery = true;
+            implementor.freeContext();
+        }
+        implementor.visitChild(this.right, this);
+        if (this.context != implementor.getContext() || ((OLAPRel) this.right).hasSubQuery()) {
+            this.hasSubQuery = true;
+            implementor.freeContext();
+        }
+
+        this.columnRowType = buildColumnRowType();
+        if (isTopJoin) {
+            this.context.afterJoin = true;
+        }
+
+        if (!this.hasSubQuery) {
+            this.context.allColumns.clear();
+            this.context.olapRowType = getRowType();
+            buildAliasMap();
+
+            // build JoinDesc
+            RexCall condition = (RexCall) this.getCondition();
+            JoinDesc join = buildJoin(condition);
+
+            JoinRelType joinRelType = this.getJoinType();
+            String joinType = joinRelType == JoinRelType.INNER ? "INNER" : joinRelType == JoinRelType.LEFT ? "LEFT" : null;
+            join.setType(joinType);
+
+            this.context.joins.add(join);
+        }
+    }
+
+    private ColumnRowType buildColumnRowType() {
+        List<TblColRef> columns = new ArrayList<TblColRef>();
+
+        OLAPRel olapLeft = (OLAPRel) this.left;
+        ColumnRowType leftColumnRowType = olapLeft.getColumnRowType();
+        columns.addAll(leftColumnRowType.getAllColumns());
+
+        OLAPRel olapRight = (OLAPRel) this.right;
+        ColumnRowType rightColumnRowType = olapRight.getColumnRowType();
+        columns.addAll(rightColumnRowType.getAllColumns());
+
+        if (columns.size() != this.rowType.getFieldCount()) {
+            throw new IllegalStateException("RowType=" + this.rowType.getFieldCount() + ", ColumnRowType=" + columns.size());
+        }
+        return new ColumnRowType(columns);
+    }
+
+    private JoinDesc buildJoin(RexCall condition) {
+        Map<TblColRef, TblColRef> joinColumns = new HashMap<TblColRef, TblColRef>();
+        translateJoinColumn(condition, joinColumns);
+
+        List<String> pks = new ArrayList<String>();
+        List<TblColRef> pkCols = new ArrayList<TblColRef>();
+        List<String> fks = new ArrayList<String>();
+        List<TblColRef> fkCols = new ArrayList<TblColRef>();
+        String factTable = context.firstTableScan.getCubeTable();
+        for (Map.Entry<TblColRef, TblColRef> columnPair : joinColumns.entrySet()) {
+            TblColRef fromCol = columnPair.getKey();
+            TblColRef toCol = columnPair.getValue();
+            if (factTable.equalsIgnoreCase(fromCol.getTable())) {
+                fks.add(fromCol.getName());
+                fkCols.add(fromCol);
+                pks.add(toCol.getName());
+                pkCols.add(toCol);
+            } else {
+                fks.add(toCol.getName());
+                fkCols.add(toCol);
+                pks.add(fromCol.getName());
+                pkCols.add(fromCol);
+            }
+        }
+
+        JoinDesc join = new JoinDesc();
+        join.setForeignKey(fks.toArray(COLUMN_ARRAY_MARKER));
+        join.setForeignKeyColumns(fkCols.toArray(new TblColRef[fkCols.size()]));
+        join.setPrimaryKey(pks.toArray(COLUMN_ARRAY_MARKER));
+        join.setPrimaryKeyColumns(pkCols.toArray(new TblColRef[pkCols.size()]));
+        return join;
+    }
+
+    private void translateJoinColumn(RexCall condition, Map<TblColRef, TblColRef> joinColumns) {
+        SqlKind kind = condition.getOperator().getKind();
+        if (kind == SqlKind.AND) {
+            for (RexNode operand : condition.getOperands()) {
+                RexCall subCond = (RexCall) operand;
+                translateJoinColumn(subCond, joinColumns);
+            }
+        } else if (kind == SqlKind.EQUALS) {
+            List<RexNode> operands = condition.getOperands();
+            RexInputRef op0 = (RexInputRef) operands.get(0);
+            TblColRef col0 = columnRowType.getColumnByIndex(op0.getIndex());
+            RexInputRef op1 = (RexInputRef) operands.get(1);
+            TblColRef col1 = columnRowType.getColumnByIndex(op1.getIndex());
+            joinColumns.put(col0, col1);
+        }
+    }
+
+    private void buildAliasMap() {
+        int size = this.rowType.getFieldList().size();
+
+        for (int i = 0; i < size; i++) {
+            RelDataTypeField field = this.rowType.getFieldList().get(i);
+            TblColRef column = this.columnRowType.getColumnByIndex(i);
+            context.storageContext.addAlias(column, field.getName());
+        }
+    }
+
+    @Override
+    public Result implement(EnumerableRelImplementor implementor, Prefer pref) {
+        Result result = null;
+        if (this.hasSubQuery) {
+            result = super.implement(implementor, pref);
+        } else {
+            PhysType physType = PhysTypeImpl.of(implementor.getTypeFactory(), getRowType(), pref.preferArray());
+
+            RelOptTable factTable = context.firstTableScan.getTable();
+            result = implementor.result(physType, Blocks.toBlock(Expressions.call(factTable.getExpression(OLAPTable.class), "executeCubeQuery", implementor.getRootExpression(), Expressions.constant(context.id))));
+        }
+
+        return result;
+    }
+
+    @Override
+    public ColumnRowType getColumnRowType() {
+        return columnRowType;
+    }
+
+    @Override
+    public void implementRewrite(RewriteImplementor implementor) {
+        implementor.visitChild(this, this.left);
+        implementor.visitChild(this, this.right);
+
+        this.rowType = this.deriveRowType();
+
+        if (this.isTopJoin && RewriteImplementor.needRewrite(this.context)) {
+            // find missed rewrite fields
+            int paramIndex = this.rowType.getFieldList().size();
+            List<RelDataTypeField> newFieldList = new LinkedList<RelDataTypeField>();
+            for (Map.Entry<String, RelDataType> rewriteField : this.context.rewriteFields.entrySet()) {
+                String fieldName = rewriteField.getKey();
+                if (this.rowType.getField(fieldName, true) == null) {
+                    RelDataType fieldType = rewriteField.getValue();
+                    RelDataTypeField newField = new RelDataTypeFieldImpl(fieldName, paramIndex++, fieldType);
+                    newFieldList.add(newField);
+                }
+            }
+
+            // rebuild row type
+            FieldInfoBuilder fieldInfo = getCluster().getTypeFactory().builder();
+            fieldInfo.addAll(this.rowType.getFieldList());
+            fieldInfo.addAll(newFieldList);
+            this.rowType = getCluster().getTypeFactory().createStructType(fieldInfo);
+            this.context.olapRowType = this.rowType;
+
+            // rebuild columns
+            this.columnRowType = this.buildColumnRowType();
+        }
+    }
+
+    @Override
+    public OLAPContext getContext() {
+        return context;
+    }
+
+    @Override
+    public boolean hasSubQuery() {
+        return this.hasSubQuery;
+    }
+
+    @Override
+    public RelTraitSet replaceTraitSet(RelTrait trait) {
+        RelTraitSet oldTraitSet = this.traitSet;
+        this.traitSet = this.traitSet.replace(trait);
+        return oldTraitSet;
+    }
+}


Mime
View raw message