kylin-commits mailing list archives

Site index · List index
Message view « Date » · « Thread »
Top « Date » · « Thread »
From luke...@apache.org
Subject [08/51] [partial] incubator-kylin git commit: migrate repo from github.com to apache git
Date Wed, 07 Jan 2015 14:46:29 GMT
http://git-wip-us.apache.org/repos/asf/incubator-kylin/blob/4b631f92/query/src/main/java/com/kylinolap/query/relnode/OLAPLimitRel.java
----------------------------------------------------------------------
diff --git a/query/src/main/java/com/kylinolap/query/relnode/OLAPLimitRel.java b/query/src/main/java/com/kylinolap/query/relnode/OLAPLimitRel.java
new file mode 100644
index 0000000..205803b
--- /dev/null
+++ b/query/src/main/java/com/kylinolap/query/relnode/OLAPLimitRel.java
@@ -0,0 +1,134 @@
+/*
+ * 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.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.EnumerableLimitRel;
+
+import org.eigenbase.rel.RelNode;
+import org.eigenbase.rel.RelWriter;
+import org.eigenbase.rel.SingleRel;
+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.rex.RexLiteral;
+import org.eigenbase.rex.RexNode;
+
+import com.google.common.base.Preconditions;
+
+/**
+ * 
+ * @author xjiang
+ * 
+ */
+public class OLAPLimitRel extends SingleRel implements OLAPRel, EnumerableRel {
+
+    private final RexNode localOffset; // avoid same name in parent class
+    private final RexNode localFetch; // avoid same name in parent class
+    private ColumnRowType columnRowType;
+    private OLAPContext context;
+
+    public OLAPLimitRel(RelOptCluster cluster, RelTraitSet traitSet, RelNode child, RexNode offset, RexNode fetch) {
+        super(cluster, traitSet, child);
+        Preconditions.checkArgument(getConvention() == OLAPRel.CONVENTION);
+        Preconditions.checkArgument(getConvention() == child.getConvention());
+        this.localOffset = offset;
+        this.localFetch = fetch;
+    }
+
+    @Override
+    public OLAPLimitRel copy(RelTraitSet traitSet, List<RelNode> inputs) {
+        return new OLAPLimitRel(getCluster(), traitSet, sole(inputs), localOffset, localFetch);
+    }
+
+    @Override
+    public RelOptCost computeSelfCost(RelOptPlanner planner) {
+        return super.computeSelfCost(planner).multiplyBy(.05);
+    }
+
+    @Override
+    public RelWriter explainTerms(RelWriter pw) {
+        return super.explainTerms(pw).itemIf("offset", localOffset, localOffset != null).itemIf("fetch", localFetch, localFetch != null);
+    }
+
+    @Override
+    public void implementOLAP(OLAPImplementor implementor) {
+        implementor.visitChild(getChild(), this);
+
+        this.columnRowType = buildColumnRowType();
+
+        this.context = implementor.getContext();
+        Number limitValue = (Number) (((RexLiteral) localFetch).getValue());
+        int limit = limitValue.intValue();
+        this.context.storageContext.setLimit(limit);
+    }
+
+    private ColumnRowType buildColumnRowType() {
+        OLAPRel olapChild = (OLAPRel) getChild();
+        ColumnRowType inputColumnRowType = olapChild.getColumnRowType();
+        return inputColumnRowType;
+    }
+
+    @Override
+    public void implementRewrite(RewriteImplementor implementor) {
+        implementor.visitChild(this, getChild());
+
+        this.rowType = this.deriveRowType();
+        this.columnRowType = buildColumnRowType();
+    }
+
+    @Override
+    public Result implement(EnumerableRelImplementor implementor, Prefer pref) {
+        OLAPRel childRel = (OLAPRel) getChild();
+        childRel.replaceTraitSet(EnumerableConvention.INSTANCE);
+
+        EnumerableLimitRel enumLimit = new EnumerableLimitRel(getCluster(), getCluster().traitSetOf(EnumerableConvention.INSTANCE), getChild(), localOffset, localFetch);
+        Result res = enumLimit.implement(implementor, pref);
+
+        childRel.replaceTraitSet(OLAPRel.CONVENTION);
+
+        return res;
+    }
+
+    @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/OLAPProjectRel.java
----------------------------------------------------------------------
diff --git a/query/src/main/java/com/kylinolap/query/relnode/OLAPProjectRel.java b/query/src/main/java/com/kylinolap/query/relnode/OLAPProjectRel.java
new file mode 100644
index 0000000..5f45e3e
--- /dev/null
+++ b/query/src/main/java/com/kylinolap/query/relnode/OLAPProjectRel.java
@@ -0,0 +1,299 @@
+/*
+ * 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.HashSet;
+import java.util.LinkedList;
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
+
+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 org.eigenbase.rel.ProjectRelBase;
+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.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.RexLiteral;
+import org.eigenbase.rex.RexNode;
+import org.eigenbase.rex.RexProgram;
+import org.eigenbase.sql.SqlOperator;
+import org.eigenbase.sql.fun.SqlCaseOperator;
+import org.eigenbase.sql.fun.SqlStdOperatorTable;
+import org.eigenbase.sql.validate.SqlUserDefinedFunction;
+
+import com.google.common.base.Preconditions;
+import com.google.common.collect.ImmutableList;
+import com.kylinolap.metadata.model.cube.TblColRef;
+import com.kylinolap.metadata.model.cube.TblColRef.InnerDataTypeEnum;
+
+/**
+ * 
+ * @author xjiang
+ * 
+ */
+public class OLAPProjectRel extends ProjectRelBase implements OLAPRel, EnumerableRel {
+
+    private OLAPContext context;
+    private List<RexNode> rewriteProjects;
+    private boolean rewriting;
+    private ColumnRowType columnRowType;
+    private boolean hasJoin;
+    private boolean afterJoin;
+    private boolean afterAggregate;
+
+    public OLAPProjectRel(RelOptCluster cluster, RelTraitSet traitSet, RelNode child, List<RexNode> exps, RelDataType rowType, int flags) {
+        super(cluster, traitSet, child, exps, rowType, flags);
+        Preconditions.checkArgument(getConvention() == OLAPRel.CONVENTION);
+        Preconditions.checkArgument(child.getConvention() == OLAPRel.CONVENTION);
+        this.rewriteProjects = exps;
+        this.hasJoin = false;
+        this.afterJoin = false;
+        this.rowType = getRowType();
+    }
+
+    @Override
+    public List<RexNode> getChildExps() {
+        return rewriteProjects;
+    }
+
+    @Override
+    public List<RexNode> getProjects() {
+        return rewriteProjects;
+    }
+
+    @Override
+    public RelOptCost computeSelfCost(RelOptPlanner planner) {
+        return super.computeSelfCost(planner).multiplyBy(.05);
+    }
+
+    @Override
+    public ProjectRelBase copy(RelTraitSet traitSet, RelNode child, List<RexNode> exps, RelDataType rowType) {
+        return new OLAPProjectRel(getCluster(), traitSet, child, exps, rowType, this.flags);
+    }
+
+    @Override
+    public void implementOLAP(OLAPImplementor implementor) {
+        implementor.visitChild(getChild(), this);
+
+        this.context = implementor.getContext();
+        this.hasJoin = context.hasJoin;
+        this.afterJoin = context.afterJoin;
+        this.afterAggregate = context.afterAggregate;
+
+        this.columnRowType = buildColumnRowType();
+    }
+
+    private ColumnRowType buildColumnRowType() {
+        List<TblColRef> columns = new ArrayList<TblColRef>();
+        List<Set<TblColRef>> sourceColumns = new ArrayList<Set<TblColRef>>();
+        OLAPRel olapChild = (OLAPRel) getChild();
+        ColumnRowType inputColumnRowType = olapChild.getColumnRowType();
+        for (int i = 0; i < this.rewriteProjects.size(); i++) {
+            RexNode rex = this.rewriteProjects.get(i);
+            RelDataTypeField columnField = this.rowType.getFieldList().get(i);
+            String fieldName = columnField.getName();
+            Set<TblColRef> sourceCollector = new HashSet<TblColRef>();
+            TblColRef column = translateRexNode(rex, inputColumnRowType, fieldName, sourceCollector);
+            columns.add(column);
+            sourceColumns.add(sourceCollector);
+        }
+        return new ColumnRowType(columns, sourceColumns);
+    }
+
+    private TblColRef translateRexNode(RexNode rexNode, ColumnRowType inputColumnRowType, String fieldName, Set<TblColRef> sourceCollector) {
+        TblColRef column = null;
+        if (rexNode instanceof RexInputRef) {
+            RexInputRef inputRef = (RexInputRef) rexNode;
+            column = translateRexInputRef(inputRef, inputColumnRowType, fieldName, sourceCollector);
+        } else if (rexNode instanceof RexLiteral) {
+            RexLiteral literal = (RexLiteral) rexNode;
+            column = translateRexLiteral(literal);
+        } else if (rexNode instanceof RexCall) {
+            RexCall call = (RexCall) rexNode;
+            column = translateRexCall(call, inputColumnRowType, fieldName, sourceCollector);
+        } else {
+            throw new IllegalStateException("Unsupport RexNode " + rexNode);
+        }
+        return column;
+    }
+
+    private TblColRef translateRexInputRef(RexInputRef inputRef, ColumnRowType inputColumnRowType, String fieldName, Set<TblColRef> sourceCollector) {
+        int index = inputRef.getIndex();
+        // check it for rewrite count
+        if (index < inputColumnRowType.size()) {
+            TblColRef column = inputColumnRowType.getColumnByIndex(index);
+            if (!column.isInnerColumn() && !this.rewriting && !this.afterAggregate) {
+                context.allColumns.add(column);
+                sourceCollector.add(column);
+            }
+            return column;
+        } else {
+            throw new IllegalStateException("Can't find " + inputRef + " from child columnrowtype " + inputColumnRowType + " with fieldname " + fieldName);
+        }
+    }
+
+    private TblColRef translateRexLiteral(RexLiteral literal) {
+        if (RexLiteral.isNullLiteral(literal)) {
+            return TblColRef.newInnerColumn("null", InnerDataTypeEnum.LITERAL);
+        } else {
+            return TblColRef.newInnerColumn(literal.getValue().toString(), InnerDataTypeEnum.LITERAL);
+        }
+
+    }
+
+    private TblColRef translateRexCall(RexCall call, ColumnRowType inputColumnRowType, String fieldName, Set<TblColRef> sourceCollector) {
+        SqlOperator operator = call.getOperator();
+        if (operator == SqlStdOperatorTable.EXTRACT_DATE) {
+            List<RexNode> extractDateOps = call.getOperands();
+            RexCall reinterpret = (RexCall) extractDateOps.get(1);
+            List<RexNode> reinterpretOps = reinterpret.getOperands();
+            RexInputRef inputRef = (RexInputRef) reinterpretOps.get(0);
+            return translateRexInputRef(inputRef, inputColumnRowType, fieldName, sourceCollector);
+        } else if (operator instanceof SqlUserDefinedFunction) {
+            if (operator.getName().equals("QUARTER")) {
+                List<RexNode> quaterOps = call.getOperands();
+                RexInputRef inputRef = (RexInputRef) quaterOps.get(0);
+                return translateRexInputRef(inputRef, inputColumnRowType, fieldName, sourceCollector);
+            }
+        } else if (operator instanceof SqlCaseOperator) {
+            for (RexNode operand : call.getOperands()) {
+                if (operand instanceof RexInputRef) {
+                    RexInputRef inputRef = (RexInputRef) operand;
+                    return translateRexInputRef(inputRef, inputColumnRowType, fieldName, sourceCollector);
+                }
+            }
+        }
+
+        for (RexNode operand : call.getOperands()) {
+            translateRexNode(operand, inputColumnRowType, fieldName, sourceCollector);
+        }
+        return TblColRef.newInnerColumn(fieldName, InnerDataTypeEnum.LITERAL);
+    }
+
+    @Override
+    public Result implement(EnumerableRelImplementor implementor, Prefer pref) {
+        EnumerableCalcRel enumCalcRel;
+
+        RelNode child = getChild();
+        if (child instanceof OLAPFilterRel) {
+            // merge project & filter
+            OLAPFilterRel filter = (OLAPFilterRel) getChild();
+            RexProgram program = RexProgram.create(filter.getChild().getRowType(), this.rewriteProjects, filter.getCondition(), this.rowType, getCluster().getRexBuilder());
+
+            enumCalcRel = new EnumerableCalcRel(getCluster(), getCluster().traitSetOf(EnumerableConvention.INSTANCE), filter.getChild(), this.rowType, program, ImmutableList.<RelCollation> of());
+        } else {
+            // keep project for tablescan
+            RexProgram program = RexProgram.create(child.getRowType(), this.rewriteProjects, null, this.rowType, getCluster().getRexBuilder());
+
+            enumCalcRel = new EnumerableCalcRel(getCluster(), getCluster().traitSetOf(EnumerableConvention.INSTANCE), child, this.rowType, program, ImmutableList.<RelCollation> of());
+        }
+
+        return enumCalcRel.implement(implementor, pref);
+    }
+
+    @Override
+    public ColumnRowType getColumnRowType() {
+        return columnRowType;
+    }
+
+    @Override
+    public void implementRewrite(RewriteImplementor implementor) {
+        implementor.visitChild(this, getChild());
+
+        this.rewriting = true;
+
+        // project before join or is just after OLAPToEnumerableConverter
+        if (!RewriteImplementor.needRewrite(this.context) || (this.hasJoin && !this.afterJoin) || this.afterAggregate) {
+            this.columnRowType = this.buildColumnRowType();
+            return;
+        }
+
+        // find missed rewrite fields
+        int paramIndex = this.rowType.getFieldList().size();
+        List<RelDataTypeField> newFieldList = new LinkedList<RelDataTypeField>();
+        List<RexNode> newExpList = new LinkedList<RexNode>();
+        ColumnRowType inputColumnRowType = ((OLAPRel) getChild()).getColumnRowType();
+
+        for (Map.Entry<String, RelDataType> rewriteField : this.context.rewriteFields.entrySet()) {
+            String rewriteFieldName = rewriteField.getKey();
+            int rowIndex = this.columnRowType.getIndexByName(rewriteFieldName);
+            if (rowIndex < 0) {
+                int inputIndex = inputColumnRowType.getIndexByName(rewriteFieldName);
+                if (inputIndex >= 0) {
+                    // new field
+                    RelDataType fieldType = rewriteField.getValue();
+                    RelDataTypeField newField = new RelDataTypeFieldImpl(rewriteFieldName, paramIndex++, fieldType);
+                    newFieldList.add(newField);
+                    // new project
+                    RelDataTypeField inputField = getChild().getRowType().getFieldList().get(inputIndex);
+                    RexInputRef newFieldRef = new RexInputRef(inputField.getIndex(), inputField.getType());
+                    newExpList.add(newFieldRef);
+                }
+            }
+        }
+
+        if (!newFieldList.isEmpty()) {
+            // rebuild projects
+            List<RexNode> newProjects = new ArrayList<RexNode>(this.rewriteProjects);
+            newProjects.addAll(newExpList);
+            this.rewriteProjects = newProjects;
+
+            // rebuild row type
+            FieldInfoBuilder fieldInfo = getCluster().getTypeFactory().builder();
+            fieldInfo.addAll(this.rowType.getFieldList());
+            fieldInfo.addAll(newFieldList);
+            this.rowType = getCluster().getTypeFactory().createStructType(fieldInfo);
+        }
+
+        // rebuild columns
+        this.columnRowType = this.buildColumnRowType();
+
+        this.rewriting = false;
+    }
+
+    @Override
+    public OLAPContext getContext() {
+        return context;
+    }
+
+    @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/OLAPRel.java
----------------------------------------------------------------------
diff --git a/query/src/main/java/com/kylinolap/query/relnode/OLAPRel.java b/query/src/main/java/com/kylinolap/query/relnode/OLAPRel.java
new file mode 100644
index 0000000..c773db4
--- /dev/null
+++ b/query/src/main/java/com/kylinolap/query/relnode/OLAPRel.java
@@ -0,0 +1,156 @@
+/*
+ * 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.Stack;
+
+import net.hydromatic.optiq.rules.java.EnumerableRel;
+import net.hydromatic.optiq.rules.java.EnumerableRelImplementor;
+
+import org.eigenbase.rel.RelNode;
+import org.eigenbase.relopt.Convention;
+import org.eigenbase.relopt.RelTrait;
+import org.eigenbase.relopt.RelTraitSet;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+/**
+ * 
+ * @author xjiang
+ * 
+ */
+public interface OLAPRel extends RelNode {
+
+    public static final Logger logger = LoggerFactory.getLogger(OLAPRel.class);
+
+    // Calling convention for relational operations that occur in OLAP.
+    public static final Convention CONVENTION = new Convention.Impl("OLAP", OLAPRel.class);
+
+    /**
+     * get olap context
+     */
+    public OLAPContext getContext();
+
+    /**
+     * get the row type of ColumnDesc
+     * 
+     * @return
+     */
+    public ColumnRowType getColumnRowType();
+
+    /**
+     * whether has sub query
+     */
+    public boolean hasSubQuery();
+
+    /**
+     * replace RelTraitSet
+     */
+    public RelTraitSet replaceTraitSet(RelTrait trait);
+
+    /**
+     * visitor pattern for olap query analysis
+     */
+    public static class OLAPImplementor {
+
+        private RelNode parentNode = null;
+        private int ctxSeq = 0;
+        private Stack<OLAPContext> ctxStack = new Stack<OLAPContext>();
+
+        public void visitChild(RelNode input, RelNode parentNode) {
+            this.parentNode = parentNode;
+            ((OLAPRel) input).implementOLAP(this);
+        }
+
+        public RelNode getParentNode() {
+            return parentNode;
+        }
+
+        public OLAPContext getContext() {
+            if (ctxStack.isEmpty()) {
+                return null;
+            }
+            return ctxStack.peek();
+        }
+
+        public void freeContext() {
+            ctxStack.pop();
+        }
+
+        public void allocateContext() {
+            OLAPContext context = new OLAPContext(ctxSeq++);
+            ctxStack.push(context);
+            OLAPContext.registerContext(context);
+        }
+    }
+
+    public void implementOLAP(OLAPImplementor implementor);
+
+    /**
+     * visitor pattern for query rewrite
+     */
+
+    public static class RewriteImplementor {
+        private OLAPContext parentContext;
+
+        public void visitChild(RelNode parent, RelNode child) {
+            if (parent instanceof OLAPRel) {
+                OLAPRel olapRel = (OLAPRel) parent;
+                this.parentContext = olapRel.getContext();
+            }
+            OLAPRel olapChild = (OLAPRel) child;
+            olapChild.implementRewrite(this);
+        }
+
+        public OLAPContext getParentContext() {
+            return parentContext;
+        }
+
+        public static boolean needRewrite(OLAPContext ctx) {
+            boolean hasFactTable = ctx.hasJoin || ctx.firstTableScan.getCubeTable().equals(ctx.cubeDesc.getFactTable());
+            boolean hasRewriteFields = !ctx.rewriteFields.isEmpty();
+            return hasRewriteFields && hasFactTable;
+        }
+    }
+
+    public void implementRewrite(RewriteImplementor rewriter);
+
+    /**
+     * implementor for java generation
+     */
+    public static class JavaImplementor extends EnumerableRelImplementor {
+
+        private OLAPContext parentContext;
+
+        public JavaImplementor(EnumerableRelImplementor enumImplementor) {
+            super(enumImplementor.getRexBuilder());
+        }
+
+        public OLAPContext getParentContext() {
+            return parentContext;
+        }
+
+        @Override
+        public EnumerableRel.Result visitChild(EnumerableRel parent, int ordinal, EnumerableRel child, EnumerableRel.Prefer prefer) {
+            if (parent instanceof OLAPRel) {
+                OLAPRel olapRel = (OLAPRel) parent;
+                this.parentContext = olapRel.getContext();
+            }
+            return super.visitChild(parent, ordinal, child, prefer);
+        }
+    }
+
+}

http://git-wip-us.apache.org/repos/asf/incubator-kylin/blob/4b631f92/query/src/main/java/com/kylinolap/query/relnode/OLAPSortRel.java
----------------------------------------------------------------------
diff --git a/query/src/main/java/com/kylinolap/query/relnode/OLAPSortRel.java b/query/src/main/java/com/kylinolap/query/relnode/OLAPSortRel.java
new file mode 100644
index 0000000..9d71eaf
--- /dev/null
+++ b/query/src/main/java/com/kylinolap/query/relnode/OLAPSortRel.java
@@ -0,0 +1,153 @@
+/*
+ * 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 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.EnumerableSortRel;
+
+import org.eigenbase.rel.RelCollation;
+import org.eigenbase.rel.RelFieldCollation;
+import org.eigenbase.rel.RelNode;
+import org.eigenbase.rel.SortRel;
+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.rex.RexNode;
+
+import com.google.common.base.Preconditions;
+import com.kylinolap.metadata.model.cube.MeasureDesc;
+import com.kylinolap.metadata.model.cube.TblColRef;
+import com.kylinolap.storage.StorageContext;
+
+/**
+ * @author xjiang
+ * 
+ */
+public class OLAPSortRel extends SortRel implements EnumerableRel, OLAPRel {
+
+    private ColumnRowType columnRowType;
+    private OLAPContext context;
+
+    public OLAPSortRel(RelOptCluster cluster, RelTraitSet traitSet, RelNode child, RelCollation collation, RexNode offset, RexNode fetch) {
+        super(cluster, traitSet, child, collation, offset, fetch);
+        Preconditions.checkArgument(getConvention() == OLAPRel.CONVENTION);
+        Preconditions.checkArgument(getConvention() == child.getConvention());
+    }
+
+    @Override
+    public OLAPSortRel copy(RelTraitSet traitSet, RelNode newInput, RelCollation newCollation, RexNode offset, RexNode fetch) {
+        return new OLAPSortRel(getCluster(), traitSet, newInput, newCollation, offset, fetch);
+    }
+
+    @Override
+    public RelOptCost computeSelfCost(RelOptPlanner planner) {
+        return super.computeSelfCost(planner).multiplyBy(.05);
+    }
+
+    @Override
+    public void implementOLAP(OLAPImplementor implementor) {
+        implementor.visitChild(getChild(), this);
+
+        this.context = implementor.getContext();
+        this.columnRowType = buildColumnRowType();
+    }
+
+    private ColumnRowType buildColumnRowType() {
+        OLAPRel olapChild = (OLAPRel) getChild();
+        ColumnRowType inputColumnRowType = olapChild.getColumnRowType();
+        return inputColumnRowType;
+    }
+
+    @Override
+    public void implementRewrite(RewriteImplementor implementor) {
+        implementor.visitChild(this, getChild());
+
+        for (RelFieldCollation fieldCollation : this.collation.getFieldCollations()) {
+            int index = fieldCollation.getFieldIndex();
+            StorageContext.OrderEnum order = getOrderEnum(fieldCollation.getDirection());
+            OLAPRel olapChild = (OLAPRel) this.getChild();
+            TblColRef orderCol = olapChild.getColumnRowType().getAllColumns().get(index);
+            MeasureDesc measure = findMeasure(orderCol);
+            if (measure != null) {
+                this.context.storageContext.addSort(measure, order);
+            }
+            this.context.storageContext.markSort();
+        }
+
+        this.rowType = this.deriveRowType();
+        this.columnRowType = buildColumnRowType();
+    }
+
+    private StorageContext.OrderEnum getOrderEnum(RelFieldCollation.Direction direction) {
+        if (direction == RelFieldCollation.Direction.DESCENDING) {
+            return StorageContext.OrderEnum.DESCENDING;
+        } else {
+            return StorageContext.OrderEnum.ASCENDING;
+        }
+    }
+
+    private MeasureDesc findMeasure(TblColRef col) {
+        for (MeasureDesc measure : this.context.cubeDesc.getMeasures()) {
+            if (col.getName().equals(measure.getFunction().getRewriteFieldName())) {
+                return measure;
+            }
+        }
+        return null;
+    }
+
+    @Override
+    public Result implement(EnumerableRelImplementor implementor, Prefer pref) {
+        OLAPRel childRel = (OLAPRel) getChild();
+        childRel.replaceTraitSet(EnumerableConvention.INSTANCE);
+
+        EnumerableSortRel enumSort = new EnumerableSortRel(getCluster(), getCluster().traitSetOf(EnumerableConvention.INSTANCE, collation), getChild(), collation, offset, fetch);
+
+        Result res = enumSort.implement(implementor, pref);
+
+        childRel.replaceTraitSet(OLAPRel.CONVENTION);
+
+        return res;
+    }
+
+    @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/OLAPTableScan.java
----------------------------------------------------------------------
diff --git a/query/src/main/java/com/kylinolap/query/relnode/OLAPTableScan.java b/query/src/main/java/com/kylinolap/query/relnode/OLAPTableScan.java
new file mode 100644
index 0000000..9c50c36
--- /dev/null
+++ b/query/src/main/java/com/kylinolap/query/relnode/OLAPTableScan.java
@@ -0,0 +1,246 @@
+/*
+ * 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.List;
+import java.util.Map;
+
+import net.hydromatic.linq4j.expressions.Blocks;
+import net.hydromatic.linq4j.expressions.Expressions;
+import net.hydromatic.linq4j.expressions.Primitive;
+import net.hydromatic.optiq.rules.java.EnumerableRel;
+import net.hydromatic.optiq.rules.java.EnumerableRelImplementor;
+import net.hydromatic.optiq.rules.java.PhysType;
+import net.hydromatic.optiq.rules.java.PhysTypeImpl;
+
+import org.eigenbase.rel.RelNode;
+import org.eigenbase.rel.RelWriter;
+import org.eigenbase.rel.TableAccessRelBase;
+import org.eigenbase.rel.rules.*;
+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;
+import org.eigenbase.reltype.RelDataTypeField;
+
+import com.google.common.base.Preconditions;
+import com.kylinolap.metadata.model.cube.TblColRef;
+import com.kylinolap.metadata.model.schema.ColumnDesc;
+import com.kylinolap.query.optrule.OLAPAggregateRule;
+import com.kylinolap.query.optrule.OLAPFilterRule;
+import com.kylinolap.query.optrule.OLAPJoinRule;
+import com.kylinolap.query.optrule.OLAPLimitRule;
+import com.kylinolap.query.optrule.OLAPProjectRule;
+import com.kylinolap.query.optrule.OLAPSortRule;
+import com.kylinolap.query.optrule.OLAPToEnumerableConverterRule;
+import com.kylinolap.query.schema.OLAPSchema;
+import com.kylinolap.query.schema.OLAPTable;
+
+/**
+ * @author xjiang
+ */
+public class OLAPTableScan extends TableAccessRelBase implements OLAPRel, EnumerableRel {
+
+    private final OLAPTable olapTable;
+    private final String cubeTable;
+    private final int[] fields;
+    private ColumnRowType columnRowType;
+    private OLAPContext context;
+
+    public OLAPTableScan(RelOptCluster cluster, RelOptTable table, OLAPTable olapTable, int[] fields) {
+        super(cluster, cluster.traitSetOf(OLAPRel.CONVENTION), table);
+        this.olapTable = olapTable;
+        this.fields = fields;
+        this.cubeTable = olapTable.getTableName();
+        this.rowType = getRowType();
+    }
+
+    public OLAPTable getOlapTable() {
+        return olapTable;
+    }
+
+    public String getCubeTable() {
+        return cubeTable;
+    }
+
+    public int[] getFields() {
+        return fields;
+    }
+
+    @Override
+    public OLAPContext getContext() {
+        return context;
+    }
+
+    @Override
+    public RelNode copy(RelTraitSet traitSet, List<RelNode> inputs) {
+        Preconditions.checkArgument(inputs.isEmpty());
+        return new OLAPTableScan(getCluster(), table, olapTable, fields);
+    }
+
+    @Override
+    public void register(RelOptPlanner planner) {
+        // force clear the query context before traversal relational operators
+        OLAPContext.clearThreadLocalContexts();
+
+        // register OLAP rules
+        planner.addRule(OLAPToEnumerableConverterRule.INSTANCE);
+        planner.addRule(OLAPFilterRule.INSTANCE);
+        planner.addRule(OLAPProjectRule.INSTANCE);
+        planner.addRule(OLAPAggregateRule.INSTANCE);
+        planner.addRule(OLAPJoinRule.INSTANCE);
+        planner.addRule(OLAPLimitRule.INSTANCE);
+        planner.addRule(OLAPSortRule.INSTANCE);
+
+        // since join is the entry point, we can't push filter past join
+        planner.removeRule(PushFilterPastJoinRule.FILTER_ON_JOIN);
+        planner.removeRule(PushFilterPastJoinRule.JOIN);
+
+        // TODO : since we don't have statistic of table, the optimization of join is too cost
+        planner.removeRule(SwapJoinRule.INSTANCE);
+        planner.removeRule(PushJoinThroughJoinRule.LEFT);
+        planner.removeRule(PushJoinThroughJoinRule.RIGHT);
+
+        // for columns in having clause will enable table scan filter rule
+        // cause kylin does not depend on MPP
+        planner.removeRule(PushFilterPastProjectRule.INSTANCE);
+        // distinct count will be split into a separated query that is joined with the left query
+        planner.removeRule(RemoveDistinctAggregateRule.INSTANCE);
+    }
+
+    @Override
+    public RelDataType deriveRowType() {
+        final List<RelDataTypeField> fieldList = table.getRowType().getFieldList();
+        final RelDataTypeFactory.FieldInfoBuilder builder = getCluster().getTypeFactory().builder();
+        for (int field : fields) {
+            builder.add(fieldList.get(field));
+        }
+        return getCluster().getTypeFactory().createStructType(builder);
+    }
+
+    @Override
+    public RelOptCost computeSelfCost(RelOptPlanner planner) {
+        return super.computeSelfCost(planner).multiplyBy(.05);
+    }
+
+    @Override
+    public RelWriter explainTerms(RelWriter pw) {
+        return super.explainTerms(pw).item("fields", Primitive.asList(fields));
+    }
+
+    @Override
+    public void implementOLAP(OLAPImplementor implementor) {
+        // create context in case of non-join
+        if (implementor.getContext() == null || !(implementor.getParentNode() instanceof OLAPJoinRel)) {
+            implementor.allocateContext();
+        }
+        columnRowType = buildColumnRowType();
+        context = implementor.getContext();
+
+        if (context.olapSchema == null) {
+            OLAPSchema schema = olapTable.getSchema();
+            context.olapSchema = schema;
+            context.storageContext.setConnUrl(schema.getStorageUrl());
+        }
+
+        if (context.firstTableScan == null) {
+            context.firstTableScan = this;
+        }
+
+        context.olapRowType = rowType;
+    }
+
+    private ColumnRowType buildColumnRowType() {
+        List<TblColRef> columns = new ArrayList<TblColRef>();
+        for (ColumnDesc sourceColumn : olapTable.getExposedColumns()) {
+            TblColRef colRef = new TblColRef(sourceColumn);
+            columns.add(colRef);
+        }
+        return new ColumnRowType(columns);
+    }
+
+    @Override
+    public Result implement(EnumerableRelImplementor implementor, Prefer pref) {
+        if (!(implementor instanceof JavaImplementor))
+            throw new IllegalStateException("implementor is not JavaImplementor");
+        JavaImplementor javaImplementor = (JavaImplementor) implementor;
+
+        int ctxId = this.context.id;
+        if (javaImplementor.getParentContext() != null) {
+            ctxId = javaImplementor.getParentContext().id;
+        }
+
+        PhysType physType = PhysTypeImpl.of(javaImplementor.getTypeFactory(), this.rowType, pref.preferArray());
+
+        String execFunction = genExecFunc();
+
+        return javaImplementor.result(physType, Blocks.toBlock(Expressions.call(table.getExpression(OLAPTable.class), execFunction, javaImplementor.getRootExpression(), Expressions.constant(ctxId))));
+    }
+
+    private String genExecFunc() {
+        // if the table to scan is not the fact table of cube, then it's a
+        // lookup table
+        if (context.hasJoin == false && cubeTable.equals(context.cubeDesc.getFactTable()) == false) {
+            return "executeLookupTableQuery";
+        } else {
+            return "executeCubeQuery";
+        }
+
+    }
+
+    @Override
+    public ColumnRowType getColumnRowType() {
+        return columnRowType;
+    }
+
+    /**
+     * Because OLAPTableScan is reused for the same table, we can't use
+     * this.context and have to use parent context
+     */
+    @Override
+    public void implementRewrite(RewriteImplementor implementor) {
+        Map<String, RelDataType> rewriteFields = this.context.rewriteFields;
+        if (implementor.getParentContext() != null) {
+            rewriteFields = implementor.getParentContext().rewriteFields;
+        }
+
+        for (Map.Entry<String, RelDataType> rewriteField : rewriteFields.entrySet()) {
+            String fieldName = rewriteField.getKey();
+            RelDataTypeField field = rowType.getField(fieldName, true);
+            if (field != null) {
+                RelDataType fieldType = field.getType();
+                rewriteField.setValue(fieldType);
+            }
+        }
+    }
+
+    @Override
+    public boolean hasSubQuery() {
+        return false;
+    }
+
+    @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/OLAPToEnumerableConverter.java
----------------------------------------------------------------------
diff --git a/query/src/main/java/com/kylinolap/query/relnode/OLAPToEnumerableConverter.java b/query/src/main/java/com/kylinolap/query/relnode/OLAPToEnumerableConverter.java
new file mode 100644
index 0000000..b38282e
--- /dev/null
+++ b/query/src/main/java/com/kylinolap/query/relnode/OLAPToEnumerableConverter.java
@@ -0,0 +1,109 @@
+/*
+ * 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.List;
+
+import net.hydromatic.linq4j.expressions.Blocks;
+import net.hydromatic.linq4j.expressions.Expressions;
+import net.hydromatic.optiq.rules.java.EnumerableRel;
+import net.hydromatic.optiq.rules.java.EnumerableRelImplementor;
+import net.hydromatic.optiq.rules.java.PhysType;
+import net.hydromatic.optiq.rules.java.PhysTypeImpl;
+
+import org.eigenbase.rel.RelNode;
+import org.eigenbase.rel.convert.ConverterRelImpl;
+import org.eigenbase.relopt.ConventionTraitDef;
+import org.eigenbase.relopt.RelOptCluster;
+import org.eigenbase.relopt.RelOptCost;
+import org.eigenbase.relopt.RelOptPlanner;
+import org.eigenbase.relopt.RelOptTable;
+import org.eigenbase.relopt.RelTraitSet;
+import org.eigenbase.reltype.RelDataType;
+
+import com.kylinolap.cube.CubeInstance;
+import com.kylinolap.query.relnode.OLAPRel.JavaImplementor;
+import com.kylinolap.query.relnode.OLAPRel.OLAPImplementor;
+import com.kylinolap.query.relnode.OLAPRel.RewriteImplementor;
+import com.kylinolap.query.routing.CubeNotFoundException;
+import com.kylinolap.query.routing.QueryRouter;
+import com.kylinolap.query.schema.OLAPTable;
+
+/**
+ * @author xjiang
+ */
+public class OLAPToEnumerableConverter extends ConverterRelImpl implements EnumerableRel {
+
+    public OLAPToEnumerableConverter(RelOptCluster cluster, RelTraitSet traits, RelNode input) {
+        super(cluster, ConventionTraitDef.INSTANCE, traits, input);
+    }
+
+    @Override
+    public RelNode copy(RelTraitSet traitSet, List<RelNode> inputs) {
+        return new OLAPToEnumerableConverter(getCluster(), traitSet, sole(inputs));
+    }
+
+    @Override
+    public RelOptCost computeSelfCost(RelOptPlanner planner) {
+        return super.computeSelfCost(planner).multiplyBy(.05);
+    }
+
+    @Override
+    public Result implement(EnumerableRelImplementor enumImplementor, Prefer pref) {
+        // post-order travel children
+        OLAPImplementor olapImplementor = new OLAPRel.OLAPImplementor();
+        olapImplementor.visitChild(getChild(), this);
+
+        // find cube from olap context
+        try {
+            for (OLAPContext context : OLAPContext.getThreadLocalContexts()) {
+                CubeInstance cube = QueryRouter.findCube(context);
+                context.cubeInstance = cube;
+                context.cubeDesc = cube.getDescriptor();
+            }
+        } catch (CubeNotFoundException e) {
+            OLAPContext ctx0 = (OLAPContext) OLAPContext.getThreadLocalContexts().toArray()[0];
+            if (ctx0 != null && ctx0.olapSchema.hasStarSchemaUrl()) {
+                // generate hive result
+                return buildHiveResult(enumImplementor, pref, ctx0);
+            } else {
+                throw e;
+            }
+        }
+
+        // rewrite query if necessary
+        RewriteImplementor rewriteImplementor = new RewriteImplementor();
+        rewriteImplementor.visitChild(this, getChild());
+
+        // build java implementation
+        EnumerableRel child = (EnumerableRel) getChild();
+        JavaImplementor javaImplementor = new JavaImplementor(enumImplementor);
+        return javaImplementor.visitChild(this, 0, child, pref);
+
+    }
+
+    private Result buildHiveResult(EnumerableRelImplementor enumImplementor, Prefer pref, OLAPContext context) {
+        RelDataType hiveRowType = getRowType();
+
+        context.olapRowType = hiveRowType;
+        PhysType physType = PhysTypeImpl.of(enumImplementor.getTypeFactory(), hiveRowType, pref.preferArray());
+
+        RelOptTable factTable = context.firstTableScan.getTable();
+        Result result = enumImplementor.result(physType, Blocks.toBlock(Expressions.call(factTable.getExpression(OLAPTable.class), "executeHiveQuery", enumImplementor.getRootExpression())));
+        return result;
+    }
+
+}

http://git-wip-us.apache.org/repos/asf/incubator-kylin/blob/4b631f92/query/src/main/java/com/kylinolap/query/routing/CubeNotFoundException.java
----------------------------------------------------------------------
diff --git a/query/src/main/java/com/kylinolap/query/routing/CubeNotFoundException.java b/query/src/main/java/com/kylinolap/query/routing/CubeNotFoundException.java
new file mode 100644
index 0000000..b8b09ff
--- /dev/null
+++ b/query/src/main/java/com/kylinolap/query/routing/CubeNotFoundException.java
@@ -0,0 +1,34 @@
+/*
+ * 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.routing;
+
+/**
+ * @author xjiang
+ * 
+ */
+public class CubeNotFoundException extends RuntimeException {
+
+    private static final long serialVersionUID = 4372584597304243555L;
+
+    public CubeNotFoundException(String message, Throwable t) {
+        super(message, t);
+    }
+
+    public CubeNotFoundException(String message) {
+        super(message);
+    }
+}

http://git-wip-us.apache.org/repos/asf/incubator-kylin/blob/4b631f92/query/src/main/java/com/kylinolap/query/routing/QueryRouter.java
----------------------------------------------------------------------
diff --git a/query/src/main/java/com/kylinolap/query/routing/QueryRouter.java b/query/src/main/java/com/kylinolap/query/routing/QueryRouter.java
new file mode 100644
index 0000000..7745c2a
--- /dev/null
+++ b/query/src/main/java/com/kylinolap/query/routing/QueryRouter.java
@@ -0,0 +1,311 @@
+/*
+ * 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.routing;
+
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.Collections;
+import java.util.Comparator;
+import java.util.HashSet;
+import java.util.Iterator;
+import java.util.List;
+import java.util.Map;
+
+import org.apache.commons.lang3.StringUtils;
+import org.eigenbase.reltype.RelDataType;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import com.kylinolap.cube.CubeInstance;
+import com.kylinolap.cube.CubeManager;
+import com.kylinolap.cube.project.ProjectManager;
+import com.kylinolap.metadata.model.cube.CubeDesc;
+import com.kylinolap.metadata.model.cube.DimensionDesc;
+import com.kylinolap.metadata.model.cube.FunctionDesc;
+import com.kylinolap.metadata.model.cube.JoinDesc;
+import com.kylinolap.metadata.model.cube.ParameterDesc;
+import com.kylinolap.metadata.model.cube.TblColRef;
+import com.kylinolap.query.relnode.OLAPContext;
+
+/**
+ * @author xjiang
+ */
+public class QueryRouter {
+
+    private static final Logger logger = LoggerFactory.getLogger(QueryRouter.class);
+
+    public static CubeInstance findCube(OLAPContext olapContext) throws CubeNotFoundException {
+
+        CubeInstance bestCube = null;
+        // NOTE: since some query has no groups and projections are the superset of groups, we choose projections.
+        ProjectManager projectManager = ProjectManager.getInstance(olapContext.olapSchema.getConfig());
+
+        if (olapContext.isSimpleQuery()) {
+            // if simple query like "select X from fact table", just return the cube with most dimensions
+            // Note that this will only succeed to get best cube if the current simple query is on fact table.
+            // Simple query on look up table is handled in OLAPTableScan.genExecFunc
+            // In other words, for simple query on lookup tables, bestCube here will be assigned null in this method
+            bestCube = findCubeWithMostDimensions(projectManager, olapContext);
+        }
+
+        if (bestCube == null) {
+            bestCube = findBestMatchCube(projectManager, olapContext);
+        }
+
+        if (bestCube == null) {
+            throw new CubeNotFoundException("Can't find cube for fact table " + olapContext.firstTableScan.getCubeTable() //
+                    + " in project " + olapContext.olapSchema.getProjectName() + " with dimensions " //
+                    + getDimensionColumns(olapContext) + " and measures " + olapContext.aggregations //
+                    + ". Also please check whether join types match what defined in Cube.");
+        }
+
+        return bestCube;
+    }
+
+    private static CubeInstance findCubeWithMostDimensions(ProjectManager projectManager, OLAPContext olapContext) {
+        List<CubeInstance> candidates = projectManager.getOnlineCubesByFactTable(olapContext.olapSchema.getProjectName(), olapContext.firstTableScan.getCubeTable());
+        if (candidates.isEmpty()) {
+            return null;
+        }
+
+        CubeInstance cubeWithMostColumns = candidates.get(0);
+        for (CubeInstance instance : candidates) {
+            int currentDimCount = instance.getDescriptor().listDimensionColumnsIncludingDerived().size();
+            int maxDimCount = cubeWithMostColumns.getDescriptor().listDimensionColumnsIncludingDerived().size();
+
+            if ((currentDimCount > maxDimCount) || ((currentDimCount == maxDimCount) && (instance.getCost() < cubeWithMostColumns.getCost())))
+                cubeWithMostColumns = instance;
+        }
+        return cubeWithMostColumns;
+    }
+
+    private static void sortByCost(List<CubeInstance> matchCubes) {
+        // sort cube candidates, 0) the cost indicator, 1) the lesser header
+        // columns the better, 2) the lesser body columns the better
+        Collections.sort(matchCubes, new Comparator<CubeInstance>() {
+            @Override
+            public int compare(CubeInstance c1, CubeInstance c2) {
+                int comp = 0;
+                comp = c1.getCost() - c2.getCost();
+                if (comp != 0) {
+                    return comp;
+                }
+
+                CubeDesc schema1 = c1.getDescriptor();
+                CubeDesc schema2 = c2.getDescriptor();
+
+                comp = schema1.listDimensionColumnsIncludingDerived().size() - schema2.listDimensionColumnsIncludingDerived().size();
+                if (comp != 0)
+                    return comp;
+
+                comp = schema1.getMeasures().size() - schema2.getMeasures().size();
+                return comp;
+            }
+        });
+    }
+
+    private static Collection<TblColRef> getDimensionColumns(OLAPContext olapContext) {
+        Collection<TblColRef> dimensionColumns = new HashSet<TblColRef>();
+        dimensionColumns.addAll(olapContext.allColumns);
+        for (TblColRef measureColumn : olapContext.metricsColumns) {
+            dimensionColumns.remove(measureColumn);
+        }
+        return dimensionColumns;
+    }
+
+    static List<CubeInstance> findMatchCubesForTableScanQuery(CubeManager cubeMgr, String factTableName, Collection<TblColRef> dimensionColumns, Collection<FunctionDesc> functions) throws CubeNotFoundException {
+        return null;
+    }
+
+    static CubeInstance findBestMatchCube(ProjectManager projectManager, OLAPContext olapContext) throws CubeNotFoundException {
+
+        // retrieve members from olapContext
+        String factTableName = olapContext.firstTableScan.getCubeTable();
+        String projectName = olapContext.olapSchema.getProjectName();
+        Collection<TblColRef> dimensionColumns = getDimensionColumns(olapContext);
+        Collection<FunctionDesc> functions = olapContext.aggregations;
+        Collection<TblColRef> metricsColumns = olapContext.metricsColumns;
+        Collection<JoinDesc> joins = olapContext.joins;
+        Map<String, RelDataType> rewriteFields = olapContext.rewriteFields;
+
+        // find cubes by table
+        List<CubeInstance> candidates = projectManager.getCubesByTable(projectName, factTableName);
+        logger.info("Find candidates by table " + factTableName + " and project=" + projectName + " : " + StringUtils.join(candidates, ","));
+
+        // match dimensions & aggregations & joins
+        Iterator<CubeInstance> it = candidates.iterator();
+        List<CubeInstance> backups = new ArrayList<CubeInstance>();
+
+        while (it.hasNext()) {
+            CubeInstance cube = it.next();
+            boolean isOnline = cube.isReady();
+
+            boolean matchDimensions = isMatchedWithDimensions(dimensionColumns, cube);
+            boolean matchAggregation = isMatchedWithAggregations(functions, cube);
+            boolean matchJoin = isMatchedWithJoins(joins, cube);
+
+            // Some cubes are not "perfectly" match, but still save them in case of usage
+            if (isOnline && matchDimensions && !matchAggregation && matchJoin) {
+                // sometimes metrics are indeed dimensions
+                // e.g. select min(cal_dt) from ..., where cal_dt is actually a dimension
+                if (isWeaklyMatchedWithAggregations(functions, metricsColumns, cube)) {
+                    logger.info("Weak matched cube " + cube);
+                    backups.add(cube);
+                }
+            }
+
+            if (!isOnline || !matchDimensions || !matchAggregation || !matchJoin) {
+                logger.info("Remove cube " + cube.getName() + " because " + " isOnlne=" + isOnline + ",matchDimensions=" + matchDimensions + ",matchAggregation=" + matchAggregation + ",matchJoin=" + matchJoin);
+                it.remove();
+            }
+        }
+
+        // normal case:
+        if (!candidates.isEmpty()) {
+            return getCheapestCube(candidates);
+        }
+        // consider backup
+        else if (!backups.isEmpty()) {
+            CubeInstance cube = getCheapestCube(backups);
+            // Using backup cubes indicates that previous judgment on dimensions/metrics is incorrect
+            adjustOLAPContext(dimensionColumns, functions, metricsColumns, cube, rewriteFields, olapContext);
+            logger.info("Use weak matched cube " + cube.getName());
+            return cube;
+        }
+        return null;
+    }
+
+    private static CubeInstance getCheapestCube(List<CubeInstance> candidates) {
+        sortByCost(candidates);
+        CubeInstance bestCube = null;
+        if (!candidates.isEmpty()) {
+            bestCube = candidates.iterator().next();
+        }
+        return bestCube;
+    }
+
+    private static boolean isMatchedWithDimensions(Collection<TblColRef> dimensionColumns, CubeInstance cube) {
+        CubeDesc cubeDesc = cube.getDescriptor();
+        boolean matchAgg = cubeDesc.listDimensionColumnsIncludingDerived().containsAll(dimensionColumns);
+        return matchAgg;
+    }
+
+    private static boolean isMatchedWithAggregations(Collection<FunctionDesc> aggregations, CubeInstance cube) {
+        CubeDesc cubeDesc = cube.getDescriptor();
+        boolean matchAgg = cubeDesc.listAllFunctions().containsAll(aggregations);
+        return matchAgg;
+    }
+
+    private static boolean isMatchedWithJoins(Collection<JoinDesc> joins, CubeInstance cube) throws CubeNotFoundException {
+        CubeDesc cubeDesc = cube.getDescriptor();
+
+        List<JoinDesc> cubeJoins = new ArrayList<JoinDesc>(cubeDesc.getDimensions().size());
+        for (DimensionDesc d : cubeDesc.getDimensions()) {
+            if (d.getJoin() != null) {
+                cubeJoins.add(d.getJoin());
+            }
+        }
+        for (JoinDesc j : joins) {
+            // optiq engine can't decide which one is fk or pk
+            String pTable = j.getPrimaryKeyColumns()[0].getTable();
+            String factTable = cubeDesc.getFactTable();
+            if (factTable.equals(pTable)) {
+                j.swapPKFK();
+            }
+
+            // check primary key, all PK column should refer to same tale, the Fact Table of cube.
+            // Using first column's table name to check.
+            String fTable = j.getForeignKeyColumns()[0].getTable();
+            if (!factTable.equals(fTable)) {
+                logger.info("Fact Table" + factTable + " not matched in join: " + j + " on cube " + cube.getName());
+                return false;
+            }
+
+            // The hashcode() function of JoinDesc has been overwritten,
+            // which takes into consideration: pk,fk,jointype
+            if (!cubeJoins.contains(j)) {
+                logger.info("Query joins don't macth on cube " + cube.getName());
+                return false;
+            }
+        }
+        return true;
+    }
+
+    private static boolean isWeaklyMatchedWithAggregations(Collection<FunctionDesc> aggregations, Collection<TblColRef> metricColumns, CubeInstance cube) {
+        CubeDesc cubeDesc = cube.getDescriptor();
+        Collection<FunctionDesc> cubeFuncs = cubeDesc.listAllFunctions();
+
+        boolean matched = true;
+        for (FunctionDesc functionDesc : aggregations) {
+            if (cubeFuncs.contains(functionDesc))
+                continue;
+
+            // only inverted-index cube does not have count, and let calcite handle in this case
+            if (functionDesc.isCount())
+                continue;
+
+            if (functionDesc.isCountDistinct()) // calcite can not handle distinct count
+                matched = false;
+
+            TblColRef col = findTblColByMetrics(metricColumns, functionDesc);
+            if (col == null || !cubeDesc.listDimensionColumnsIncludingDerived().contains(col)) {
+                matched = false;
+            }
+        }
+        return matched;
+    }
+
+    private static void adjustOLAPContext(Collection<TblColRef> dimensionColumns, Collection<FunctionDesc> aggregations, //
+            Collection<TblColRef> metricColumns, CubeInstance cube, Map<String, RelDataType> rewriteFields, OLAPContext olapContext) {
+        CubeDesc cubeDesc = cube.getDescriptor();
+        Collection<FunctionDesc> cubeFuncs = cubeDesc.listAllFunctions();
+
+        Iterator<FunctionDesc> it = aggregations.iterator();
+        while (it.hasNext()) {
+            FunctionDesc functionDesc = it.next();
+            if (!cubeFuncs.contains(functionDesc)) {
+                // try to convert the metric to dimension to see if it works
+                TblColRef col = findTblColByMetrics(metricColumns, functionDesc);
+                functionDesc.setAppliedOnDimension(true);
+                rewriteFields.remove(functionDesc.getRewriteFieldName());
+                if (col != null) {
+                    metricColumns.remove(col);
+                    dimensionColumns.add(col);
+                    olapContext.storageContext.addOtherMandatoryColumns(col);
+                }
+                logger.info("Adjust OLAPContext for " + functionDesc);
+            }
+        }
+    }
+
+    private static TblColRef findTblColByMetrics(Collection<TblColRef> dimensionColumns, FunctionDesc func) {
+        if (func.isCount())
+            return null; // count is not about any column but the whole row
+
+        ParameterDesc parameter = func.getParameter();
+        if (parameter == null)
+            return null;
+
+        String columnName = parameter.getValue();
+        for (TblColRef col : dimensionColumns) {
+            String name = col.getName();
+            if (name != null && name.equals(columnName))
+                return col;
+        }
+        return null;
+    }
+
+}

http://git-wip-us.apache.org/repos/asf/incubator-kylin/blob/4b631f92/query/src/main/java/com/kylinolap/query/schema/OLAPSchema.java
----------------------------------------------------------------------
diff --git a/query/src/main/java/com/kylinolap/query/schema/OLAPSchema.java b/query/src/main/java/com/kylinolap/query/schema/OLAPSchema.java
new file mode 100644
index 0000000..0bb7689
--- /dev/null
+++ b/query/src/main/java/com/kylinolap/query/schema/OLAPSchema.java
@@ -0,0 +1,123 @@
+/*
+ * 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.schema;
+
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+
+import net.hydromatic.optiq.Table;
+import net.hydromatic.optiq.impl.AbstractSchema;
+
+import com.kylinolap.common.KylinConfig;
+import com.kylinolap.cube.CubeManager;
+import com.kylinolap.cube.project.ProjectInstance;
+import com.kylinolap.cube.project.ProjectManager;
+import com.kylinolap.metadata.MetadataManager;
+import com.kylinolap.metadata.model.schema.TableDesc;
+
+/**
+ * @author xjiang
+ */
+public class OLAPSchema extends AbstractSchema {
+
+//    private static final Logger logger = LoggerFactory.getLogger(OLAPSchema.class);
+
+    private KylinConfig config;
+    private String projectName;
+    private String schemaName;
+    private String storageUrl;
+    private String starSchemaUrl;
+    private String starSchemaUser;
+    private String starSchemaPassword;
+
+    private void init() {
+        this.config = KylinConfig.getInstanceFromEnv();
+        this.storageUrl = config.getStorageUrl();
+        this.starSchemaUrl = config.getHiveUrl();
+        this.starSchemaUser = config.getHiveUser();
+        this.starSchemaPassword = config.getHivePassword();
+    }
+
+    public OLAPSchema(String project, String schemaName) {
+        this.projectName = ProjectInstance.getNormalizedProjectName(project);
+        this.schemaName = schemaName;
+        init();
+    }
+
+    @Override
+    protected Map<String, Table> getTableMap() {
+        return buildTableMap();
+    }
+
+    private Map<String, Table> buildTableMap() {
+        Map<String, Table> olapTables = new HashMap<String, Table>();
+        List<TableDesc> projectTables = getProjectManager().listExposedTables(projectName);
+
+        for (TableDesc tableDesc : projectTables) {
+            final String tableName = tableDesc.getName();
+            final OLAPTable table = new OLAPTable(this, tableDesc);
+            olapTables.put(tableName, table);
+//            logger.debug("Project " + projectName + " exposes table " + tableName);
+        }
+
+        return olapTables;
+    }
+
+    public String getSchemaName() {
+        return schemaName;
+    }
+
+    public String getStorageUrl() {
+        return storageUrl;
+    }
+
+    public boolean hasStarSchemaUrl() {
+        return starSchemaUrl != null && !starSchemaUrl.isEmpty();
+    }
+
+    public String getStarSchemaUrl() {
+        return starSchemaUrl;
+    }
+
+    public String getStarSchemaUser() {
+        return starSchemaUser;
+    }
+
+    public String getStarSchemaPassword() {
+        return starSchemaPassword;
+    }
+
+    public MetadataManager getMetadataManager() {
+        return MetadataManager.getInstance(config);
+    }
+
+    public KylinConfig getConfig() {
+        return config;
+    }
+
+    public String getProjectName() {
+        return this.projectName;
+    }
+
+    public CubeManager getCubeManager() {
+        return CubeManager.getInstance(config);
+    }
+
+    public ProjectManager getProjectManager() {
+        return ProjectManager.getInstance(config);
+    }
+}

http://git-wip-us.apache.org/repos/asf/incubator-kylin/blob/4b631f92/query/src/main/java/com/kylinolap/query/schema/OLAPSchemaFactory.java
----------------------------------------------------------------------
diff --git a/query/src/main/java/com/kylinolap/query/schema/OLAPSchemaFactory.java b/query/src/main/java/com/kylinolap/query/schema/OLAPSchemaFactory.java
new file mode 100644
index 0000000..f3dcb8e
--- /dev/null
+++ b/query/src/main/java/com/kylinolap/query/schema/OLAPSchemaFactory.java
@@ -0,0 +1,127 @@
+/*
+ * 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.schema;
+
+import java.io.File;
+import java.io.FileWriter;
+import java.io.IOException;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+
+import net.hydromatic.optiq.Schema;
+import net.hydromatic.optiq.SchemaFactory;
+import net.hydromatic.optiq.SchemaPlus;
+
+import org.eigenbase.util14.ConversionUtil;
+
+import com.kylinolap.common.KylinConfig;
+import com.kylinolap.cube.project.ProjectInstance;
+import com.kylinolap.cube.project.ProjectManager;
+import com.kylinolap.metadata.model.schema.DatabaseDesc;
+import com.kylinolap.metadata.model.schema.TableDesc;
+
+/**
+ * @author xjiang
+ */
+public class OLAPSchemaFactory implements SchemaFactory {
+
+    static {
+        /*
+         * Tricks Optiq to work with Unicode.
+         * 
+         * Sets default char set for string literals in SQL and row types of
+         * RelNode. This is more a label used to compare row type equality. For
+         * both SQL string and row record, they are passed to Optiq in String
+         * object and does not require additional codec.
+         * 
+         * Ref SaffronProperties.defaultCharset
+         * SqlUtil.translateCharacterSetName() NlsString constructor()
+         */
+        System.setProperty("saffron.default.charset", ConversionUtil.NATIVE_UTF16_CHARSET_NAME);
+        System.setProperty("saffron.default.nationalcharset", ConversionUtil.NATIVE_UTF16_CHARSET_NAME);
+        System.setProperty("saffron.default.collation.name", ConversionUtil.NATIVE_UTF16_CHARSET_NAME + "$en_US");
+    }
+
+    private final static String SCHEMA_PROJECT = "project";
+
+    @Override
+    public Schema create(SchemaPlus parentSchema, String schemaName, Map<String, Object> operand) {
+        String project = (String) operand.get(SCHEMA_PROJECT);
+        Schema newSchema = new OLAPSchema(project, schemaName);
+        return newSchema;
+    }
+
+    public static File createTempOLAPJson(String project, KylinConfig config) {
+        project = ProjectInstance.getNormalizedProjectName(project);
+
+        List<TableDesc> tables = ProjectManager.getInstance(config).listExposedTables(project);
+        // "database" in TableDesc correspond to our schema
+        HashMap<String, Integer> schemaCounts = DatabaseDesc.extractDatabaseOccurenceCounts(tables);
+
+        String majoritySchemaName = "";
+        int majoritySchemaCount = 0;
+        for (Map.Entry<String, Integer> e : schemaCounts.entrySet()) {
+            if (e.getValue() >= majoritySchemaCount) {
+                majoritySchemaCount = e.getValue();
+                majoritySchemaName = e.getKey();
+            }
+        }
+
+        try {
+            File tmp = File.createTempFile("olap_model_", ".json");
+
+            FileWriter out = new FileWriter(tmp);
+            out.write("{\n");
+            out.write("    \"version\": \"1.0\",\n");
+            out.write("    \"defaultSchema\": \"" + majoritySchemaName + "\",\n");
+            out.write("    \"schemas\": [\n");
+
+            int counter = 0;
+            for (String schemaName : schemaCounts.keySet()) {
+                out.write("        {\n");
+                out.write("            \"type\": \"custom\",\n");
+                out.write("            \"name\": \"" + schemaName + "\",\n");
+                out.write("            \"factory\": \"com.kylinolap.query.schema.OLAPSchemaFactory\",\n");
+                out.write("            \"operand\": {\n");
+                out.write("                \"" + SCHEMA_PROJECT + "\": \"" + project + "\"\n");
+                out.write("            },\n");
+                out.write("           \"functions\": [\n");
+                out.write("                 {\n");
+                out.write("                     \"name\": \"QUARTER\",\n");
+                out.write("                     \"className\": \"com.kylinolap.query.sqlfunc.QuarterFunc\"\n");
+                out.write("                 }\n");
+                out.write("            ]\n");
+                out.write("        }\n");
+
+                if (++counter != schemaCounts.size()) {
+                    out.write(",\n");
+                }
+            }
+
+            out.write("    ]\n");
+            out.write("}\n");
+            out.close();
+
+            tmp.deleteOnExit();
+            return tmp;
+
+        } catch (IOException e) {
+            throw new RuntimeException(e);
+        }
+    }
+
+}

http://git-wip-us.apache.org/repos/asf/incubator-kylin/blob/4b631f92/query/src/main/java/com/kylinolap/query/schema/OLAPTable.java
----------------------------------------------------------------------
diff --git a/query/src/main/java/com/kylinolap/query/schema/OLAPTable.java b/query/src/main/java/com/kylinolap/query/schema/OLAPTable.java
new file mode 100644
index 0000000..c56c087
--- /dev/null
+++ b/query/src/main/java/com/kylinolap/query/schema/OLAPTable.java
@@ -0,0 +1,235 @@
+/*
+ * 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.schema;
+
+import java.util.ArrayList;
+import java.util.BitSet;
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.List;
+import java.util.Map;
+
+import net.hydromatic.linq4j.Enumerable;
+import net.hydromatic.linq4j.Enumerator;
+import net.hydromatic.linq4j.QueryProvider;
+import net.hydromatic.linq4j.Queryable;
+import net.hydromatic.optiq.DataContext;
+import net.hydromatic.optiq.SchemaPlus;
+import net.hydromatic.optiq.Statistic;
+import net.hydromatic.optiq.Statistics;
+import net.hydromatic.optiq.TranslatableTable;
+import net.hydromatic.optiq.impl.AbstractTableQueryable;
+import net.hydromatic.optiq.impl.java.AbstractQueryableTable;
+
+import org.eigenbase.rel.RelNode;
+import org.eigenbase.relopt.RelOptTable;
+import org.eigenbase.relopt.RelOptTable.ToRelContext;
+import org.eigenbase.reltype.RelDataType;
+import org.eigenbase.reltype.RelDataTypeFactory;
+import org.eigenbase.sql.type.SqlTypeName;
+import org.eigenbase.sql.type.SqlTypeUtil;
+
+import com.kylinolap.cube.project.ProjectManager;
+import com.kylinolap.metadata.model.cube.FunctionDesc;
+import com.kylinolap.metadata.model.cube.MeasureDesc;
+import com.kylinolap.metadata.model.schema.ColumnDesc;
+import com.kylinolap.metadata.model.schema.TableDesc;
+import com.kylinolap.query.enumerator.OLAPQuery;
+import com.kylinolap.query.enumerator.OLAPQuery.EnumeratorTypeEnum;
+import com.kylinolap.query.relnode.OLAPTableScan;
+
+/**
+ * 
+ * @author xjiang
+ * 
+ */
+public class OLAPTable extends AbstractQueryableTable implements TranslatableTable {
+
+    private static Map<String, SqlTypeName> SQLTYPE_MAPPING = new HashMap<String, SqlTypeName>();
+
+    static {
+        SQLTYPE_MAPPING.put("char", SqlTypeName.CHAR);
+        SQLTYPE_MAPPING.put("varchar", SqlTypeName.VARCHAR);
+        SQLTYPE_MAPPING.put("boolean", SqlTypeName.BOOLEAN);
+        SQLTYPE_MAPPING.put("integer", SqlTypeName.INTEGER);
+        SQLTYPE_MAPPING.put("tinyint", SqlTypeName.TINYINT);
+        SQLTYPE_MAPPING.put("smallint", SqlTypeName.SMALLINT);
+        SQLTYPE_MAPPING.put("bigint", SqlTypeName.BIGINT);
+        SQLTYPE_MAPPING.put("decimal", SqlTypeName.DECIMAL);
+        SQLTYPE_MAPPING.put("numeric", SqlTypeName.DECIMAL);
+        SQLTYPE_MAPPING.put("float", SqlTypeName.FLOAT);
+        SQLTYPE_MAPPING.put("real", SqlTypeName.REAL);
+        SQLTYPE_MAPPING.put("double", SqlTypeName.DOUBLE);
+        SQLTYPE_MAPPING.put("date", SqlTypeName.DATE);
+        SQLTYPE_MAPPING.put("time", SqlTypeName.TIME);
+        SQLTYPE_MAPPING.put("timestamp", SqlTypeName.TIMESTAMP);
+        SQLTYPE_MAPPING.put("any", SqlTypeName.ANY);
+
+        // try {
+        // Class.forName("org.apache.hive.jdbc.HiveDriver");
+        // } catch (ClassNotFoundException e) {
+        // e.printStackTrace();
+        // }
+    }
+
+    private final OLAPSchema olapSchema;
+    private final TableDesc sourceTable;
+    private RelDataType rowType;
+    private List<ColumnDesc> exposedColumns;
+
+    public OLAPTable(OLAPSchema schema, TableDesc tableDesc) {
+        super(Object[].class);
+        this.olapSchema = schema;
+        this.sourceTable = tableDesc;
+        this.rowType = null;
+    }
+
+    public OLAPSchema getSchema() {
+        return this.olapSchema;
+    }
+
+    public TableDesc getSourceTable() {
+        return this.sourceTable;
+    }
+
+    public String getTableName() {
+        return this.sourceTable.getName();
+    }
+
+    public List<ColumnDesc> getExposedColumns() {
+        return exposedColumns;
+    }
+
+    @Override
+    public RelDataType getRowType(RelDataTypeFactory typeFactory) {
+        if (this.rowType == null) {
+            // always build exposedColumns and rowType together
+            this.exposedColumns = listSourceColumns();
+            this.rowType = deduceRowType(typeFactory);
+        }
+        return this.rowType;
+    }
+
+    private RelDataType deduceRowType(RelDataTypeFactory typeFactory) {
+        RelDataTypeFactory.FieldInfoBuilder fieldInfo = typeFactory.builder();
+        for (ColumnDesc column : exposedColumns) {
+            RelDataType sqlType = createSqlType(typeFactory, column);
+            sqlType = SqlTypeUtil.addCharsetAndCollation(sqlType, typeFactory);
+            fieldInfo.add(column.getName(), sqlType);
+        }
+        return typeFactory.createStructType(fieldInfo);
+    }
+
+    private RelDataType createSqlType(RelDataTypeFactory typeFactory, ColumnDesc column) {
+        SqlTypeName sqlTypeName = SQLTYPE_MAPPING.get(column.getTypeName());
+        if (sqlTypeName == null)
+            throw new IllegalArgumentException("Unrecognized column type " + column.getTypeName() + " from " + column);
+
+        int precision = column.getTypePrecision();
+        int scale = column.getTypeScale();
+
+        RelDataType result;
+        if (precision >= 0 && scale >= 0)
+            result = typeFactory.createSqlType(sqlTypeName, precision, scale);
+        else if (precision >= 0)
+            result = typeFactory.createSqlType(sqlTypeName, precision);
+        else
+            result = typeFactory.createSqlType(sqlTypeName);
+
+        // due to left join and uncertain data quality, dimension value can be
+        // null
+        if (column.isNullable()) {
+            result = typeFactory.createTypeWithNullability(result, true);
+        } else {
+            result = typeFactory.createTypeWithNullability(result, false);
+        }
+
+        return result;
+    }
+
+    private List<ColumnDesc> listSourceColumns() {
+        ProjectManager projectMgr = olapSchema.getProjectManager();
+        List<ColumnDesc> exposedColumns = projectMgr.listExposedColumns(olapSchema.getProjectName(), sourceTable.getName());
+
+        List<MeasureDesc> countMeasures = projectMgr.listEffectiveRewriteMeasures(olapSchema.getProjectName(), sourceTable.getName());
+        HashSet<String> metFields = new HashSet<String>();
+        for (MeasureDesc m : countMeasures) {
+            FunctionDesc func = m.getFunction();
+            String fieldName = func.getRewriteFieldName();
+            if (metFields.contains(fieldName) == false) {
+                metFields.add(fieldName);
+                ColumnDesc fakeCountCol = new ColumnDesc();
+                fakeCountCol.setName(fieldName);
+                fakeCountCol.setDatatype(func.getSQLType());
+                fakeCountCol.setNullable(false);
+                fakeCountCol.init(sourceTable);
+                exposedColumns.add(fakeCountCol);
+            }
+        }
+
+        return exposedColumns;
+    }
+
+    @Override
+    public RelNode toRel(ToRelContext context, RelOptTable relOptTable) {
+        int fieldCount = relOptTable.getRowType().getFieldCount();
+        int[] fields = identityList(fieldCount);
+        return new OLAPTableScan(context.getCluster(), relOptTable, this, fields);
+    }
+
+    private int[] identityList(int n) {
+        int[] integers = new int[n];
+        for (int i = 0; i < n; i++) {
+            integers[i] = i;
+        }
+        return integers;
+    }
+
+    @Override
+    public <T> Queryable<T> asQueryable(QueryProvider queryProvider, SchemaPlus schema, String tableName) {
+        return new AbstractTableQueryable<T>(queryProvider, schema, this, tableName) {
+            @SuppressWarnings("unchecked")
+            public Enumerator<T> enumerator() {
+                final OLAPQuery query = new OLAPQuery(EnumeratorTypeEnum.CUBE, 0);
+                return (Enumerator<T>) query.enumerator();
+            }
+        };
+    }
+
+    @Override
+    public Statistic getStatistic() {
+        List<BitSet> keys = new ArrayList<BitSet>();
+        return Statistics.of(100, keys);
+    }
+
+    @Override
+    public String toString() {
+        return "OLAPTable {" + getTableName() + "}";
+    }
+
+    public Enumerable<Object[]> executeCubeQuery(DataContext optiqContext, int ctxSeq) {
+        return new OLAPQuery(optiqContext, EnumeratorTypeEnum.CUBE, ctxSeq);
+    }
+
+    public Enumerable<Object[]> executeLookupTableQuery(DataContext optiqContext, int ctxSeq) {
+        return new OLAPQuery(optiqContext, EnumeratorTypeEnum.LOOKUP_TABLE, ctxSeq);
+    }
+
+    public Enumerable<Object[]> executeHiveQuery(DataContext optiqContext, int ctxSeq) {
+        return new OLAPQuery(optiqContext, EnumeratorTypeEnum.HIVE, ctxSeq);
+    }
+
+}

http://git-wip-us.apache.org/repos/asf/incubator-kylin/blob/4b631f92/query/src/main/java/com/kylinolap/query/sqlfunc/HLLDistinctCountAggFunc.java
----------------------------------------------------------------------
diff --git a/query/src/main/java/com/kylinolap/query/sqlfunc/HLLDistinctCountAggFunc.java b/query/src/main/java/com/kylinolap/query/sqlfunc/HLLDistinctCountAggFunc.java
new file mode 100644
index 0000000..e8f952a
--- /dev/null
+++ b/query/src/main/java/com/kylinolap/query/sqlfunc/HLLDistinctCountAggFunc.java
@@ -0,0 +1,151 @@
+/*
+ * 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.sqlfunc;
+
+import java.io.IOException;
+import java.nio.ByteBuffer;
+
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import com.kylinolap.common.hll.HyperLogLogPlusCounter;
+
+/**
+ * @author xjiang
+ */
+public class HLLDistinctCountAggFunc {
+
+    private static final Logger logger = LoggerFactory.getLogger(HLLDistinctCountAggFunc.class);
+
+    public static HyperLogLogPlusCounter init() {
+        return null;
+    }
+
+    public static HyperLogLogPlusCounter initAdd(Object v) {
+        if (v instanceof Long) { // holistic case
+            long l = (Long) v;
+            return new FixedValueHLLCMockup(l);
+        } else {
+            HyperLogLogPlusCounter c = (HyperLogLogPlusCounter) v;
+            return new HyperLogLogPlusCounter(c);
+        }
+    }
+
+    public static HyperLogLogPlusCounter add(HyperLogLogPlusCounter counter, Object v) {
+        if (v instanceof Long) { // holistic case
+            long l = (Long) v;
+            if (counter == null) {
+                return new FixedValueHLLCMockup(l);
+            } else {
+                if (!(counter instanceof FixedValueHLLCMockup))
+                    throw new IllegalStateException("counter is not FixedValueHLLCMockup");
+
+                ((FixedValueHLLCMockup) counter).set(l);
+                return counter;
+            }
+        } else {
+            HyperLogLogPlusCounter c = (HyperLogLogPlusCounter) v;
+            if (counter == null) {
+                return new HyperLogLogPlusCounter(c);
+            } else {
+                counter.merge(c);
+                return counter;
+            }
+        }
+    }
+
+    public static HyperLogLogPlusCounter merge(HyperLogLogPlusCounter counter0, Object counter1) {
+        return add(counter0, counter1);
+    }
+
+    public static long result(HyperLogLogPlusCounter counter) {
+        return counter == null ? 0L : counter.getCountEstimate();
+    }
+
+    private static class FixedValueHLLCMockup extends HyperLogLogPlusCounter {
+
+        private Long value = null;
+
+        FixedValueHLLCMockup(long value) {
+            this.value = value;
+        }
+
+        public void set(long value) {
+            if (this.value == null) {
+                this.value = value;
+            } else {
+                long oldValue = Math.abs(this.value.longValue());
+                long take = Math.max(oldValue, value);
+                logger.warn("Error to aggregate holistic count distinct, old value " + oldValue + ", new value " + value + ", taking " + take);
+                this.value = -take; // make it obvious that this value is wrong
+            }
+        }
+
+        @Override
+        public void clear() {
+            this.value = null;
+        }
+
+        @Override
+        protected void add(long hash) {
+            throw new UnsupportedOperationException();
+        }
+
+        @Override
+        public void merge(HyperLogLogPlusCounter another) {
+            throw new UnsupportedOperationException();
+        }
+
+        @Override
+        public long getCountEstimate() {
+            return value;
+        }
+
+        @Override
+        public void writeRegisters(ByteBuffer out) throws IOException {
+            throw new UnsupportedOperationException();
+        }
+
+        @Override
+        public void readRegisters(ByteBuffer in) throws IOException {
+            throw new UnsupportedOperationException();
+        }
+
+        @Override
+        public int hashCode() {
+            final int prime = 31;
+            int result = super.hashCode();
+            result = prime * result + (int) (value ^ (value >>> 32));
+            return result;
+        }
+
+        @Override
+        public boolean equals(Object obj) {
+            if (this == obj)
+                return true;
+            if (!super.equals(obj))
+                return false;
+            if (getClass() != obj.getClass())
+                return false;
+            FixedValueHLLCMockup other = (FixedValueHLLCMockup) obj;
+            if (!value.equals(other.value))
+                return false;
+            return true;
+        }
+    }
+
+}

http://git-wip-us.apache.org/repos/asf/incubator-kylin/blob/4b631f92/query/src/main/java/com/kylinolap/query/sqlfunc/QuarterBase.java
----------------------------------------------------------------------
diff --git a/query/src/main/java/com/kylinolap/query/sqlfunc/QuarterBase.java b/query/src/main/java/com/kylinolap/query/sqlfunc/QuarterBase.java
new file mode 100644
index 0000000..f0ff187
--- /dev/null
+++ b/query/src/main/java/com/kylinolap/query/sqlfunc/QuarterBase.java
@@ -0,0 +1,20 @@
+package com.kylinolap.query.sqlfunc;
+
+import net.hydromatic.optiq.runtime.SqlFunctions;
+import net.hydromatic.optiq.runtime.SqlFunctions.TimeUnitRange;
+
+/**
+ * @author xjiang
+ * 
+ */
+public abstract class QuarterBase {
+    
+    /**
+     * According to jvm spec, it return self method before parent.
+     * So, we keep Date in parent and int in child
+     */
+    public static long eval(int date) {
+        long month = SqlFunctions.unixDateExtract(TimeUnitRange.MONTH, date);
+        return month / 4 + 1;
+    }
+}

http://git-wip-us.apache.org/repos/asf/incubator-kylin/blob/4b631f92/query/src/main/java/com/kylinolap/query/sqlfunc/QuarterFunc.java
----------------------------------------------------------------------
diff --git a/query/src/main/java/com/kylinolap/query/sqlfunc/QuarterFunc.java b/query/src/main/java/com/kylinolap/query/sqlfunc/QuarterFunc.java
new file mode 100644
index 0000000..1d72a20
--- /dev/null
+++ b/query/src/main/java/com/kylinolap/query/sqlfunc/QuarterFunc.java
@@ -0,0 +1,32 @@
+/*
+ * 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.sqlfunc;
+
+import java.sql.Date;
+
+/**
+ * @author xjiang
+ * 
+ */
+public class QuarterFunc extends QuarterBase {
+    private QuarterFunc() {
+    }
+
+    public static long eval(Date date) {
+        throw new UnsupportedOperationException();
+    }
+}


Mime
View raw message