You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@kylin.apache.org by lu...@apache.org on 2015/01/07 15:46:30 UTC
[09/51] [partial] incubator-kylin git commit: migrate repo from
github.com to apache git
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;
+ }
+}