You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@kylin.apache.org by li...@apache.org on 2015/07/22 06:13:09 UTC

[19/47] incubator-kylin git commit: KYLIN-875 rename modules: core-common, core-cube, core-dictionary, core-cube

http://git-wip-us.apache.org/repos/asf/incubator-kylin/blob/7e8896ac/core-metadata/src/main/java/org/apache/kylin/metadata/model/DataModelDesc.java
----------------------------------------------------------------------
diff --git a/core-metadata/src/main/java/org/apache/kylin/metadata/model/DataModelDesc.java b/core-metadata/src/main/java/org/apache/kylin/metadata/model/DataModelDesc.java
new file mode 100644
index 0000000..c3c9352
--- /dev/null
+++ b/core-metadata/src/main/java/org/apache/kylin/metadata/model/DataModelDesc.java
@@ -0,0 +1,310 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you 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 org.apache.kylin.metadata.model;
+
+import java.util.*;
+
+import org.apache.commons.lang.ArrayUtils;
+
+import com.fasterxml.jackson.annotation.JsonAutoDetect;
+import com.fasterxml.jackson.annotation.JsonAutoDetect.Visibility;
+import com.fasterxml.jackson.annotation.JsonProperty;
+import com.google.common.collect.Sets;
+import org.apache.kylin.common.persistence.ResourceStore;
+import org.apache.kylin.common.persistence.RootPersistentEntity;
+import org.apache.kylin.common.util.StringUtil;
+import org.apache.kylin.metadata.MetadataConstants;
+
+@JsonAutoDetect(fieldVisibility = Visibility.NONE, getterVisibility = Visibility.NONE, isGetterVisibility = Visibility.NONE, setterVisibility = Visibility.NONE)
+public class DataModelDesc extends RootPersistentEntity {
+
+    public static enum RealizationCapacity {
+        SMALL, MEDIUM, LARGE
+    }
+
+    @JsonProperty("name")
+    private String name;
+
+    @JsonProperty("description")
+    private String description;
+
+    @JsonProperty("fact_table")
+    private String factTable;
+
+    @JsonProperty("lookups")
+    private LookupDesc[] lookups;
+
+    @JsonProperty("dimensions")
+    private List<DimensionDesc> dimensions;
+
+    @JsonProperty("metrics")
+    private String[] metrics;
+
+    @JsonProperty("filter_condition")
+    private String filterCondition;
+    @JsonProperty("partition_desc")
+    PartitionDesc partitionDesc;
+
+    @JsonProperty("capacity")
+    private RealizationCapacity capacity = RealizationCapacity.MEDIUM;
+
+
+    /**
+     * Error messages during resolving json metadata
+     */
+    private List<String> errors = new ArrayList<String>();
+
+
+    public String getName() {
+        return name;
+    }
+
+    public void setName(String name) {
+        this.name = name;
+    }
+
+    public String getDescription() {
+        return description;
+    }
+
+    public void setDescription(String description) {
+        this.description = description;
+    }
+
+    public Collection<String> getAllTables() {
+        HashSet<String> ret = Sets.newHashSet();
+        ret.add(factTable);
+        for (LookupDesc lookupDesc : lookups)
+            ret.add(lookupDesc.getTable());
+        return ret;
+    }
+
+    public String getFactTable() {
+        return factTable;
+    }
+
+    public void setFactTable(String factTable) {
+        this.factTable = factTable.toUpperCase();
+    }
+
+    public LookupDesc[] getLookups() {
+        return lookups;
+    }
+
+    public void setLookups(LookupDesc[] lookups) {
+        this.lookups = lookups;
+    }
+
+    public boolean isFactTable(String factTable) {
+        return this.factTable.equalsIgnoreCase(factTable);
+    }
+
+    public String getFilterCondition() {
+        return filterCondition;
+    }
+
+    public void setFilterCondition(String filterCondition) {
+        this.filterCondition = filterCondition;
+    }
+
+    public PartitionDesc getPartitionDesc() {
+        return partitionDesc;
+    }
+
+    public void setPartitionDesc(PartitionDesc partitionDesc) {
+        this.partitionDesc = partitionDesc;
+    }
+
+    public RealizationCapacity getCapacity() {
+        return capacity;
+    }
+
+    public void setCapacity(RealizationCapacity capacity) {
+        this.capacity = capacity;
+    }
+
+    public TblColRef findPKByFK(TblColRef fk, String joinType) {
+        assert isFactTable(fk.getTable());
+
+        TblColRef candidate = null;
+
+        for (LookupDesc dim : lookups) {
+            JoinDesc join = dim.getJoin();
+            if (join == null)
+                continue;
+
+            if(joinType != null && !joinType.equals(join.getType()))
+                continue;
+
+            int find = ArrayUtils.indexOf(join.getForeignKeyColumns(), fk);
+            if (find >= 0) {
+                candidate = join.getPrimaryKeyColumns()[find];
+                if (join.getForeignKeyColumns().length == 1) { // is single
+                    // column join?
+                    break;
+                }
+            }
+        }
+        return candidate;
+    }
+
+    public void init(Map<String, TableDesc> tables) {
+        initJoinColumns(tables);
+        DimensionDesc.capicalizeStrings(dimensions);
+        initPartitionDesc(tables);
+    }
+
+    private void initPartitionDesc(Map<String, TableDesc> tables) {
+        if (this.partitionDesc != null)
+            this.partitionDesc.init(tables);
+    }
+
+    private void initJoinColumns(Map<String, TableDesc> tables) {
+        // join columns may or may not present in cube;
+        // here we don't modify 'allColumns' and 'dimensionColumns';
+        // initDimensionColumns() will do the update
+        for (LookupDesc lookup : this.lookups) {
+            lookup.setTable(lookup.getTable().toUpperCase());
+            TableDesc dimTable = tables.get(lookup.getTable());
+            if (dimTable == null) {
+                throw new IllegalStateException("Table " + lookup.getTable() + " does not exist for " + this);
+            }
+
+            JoinDesc join = lookup.getJoin();
+            if (join == null)
+                continue;
+
+            StringUtil.toUpperCaseArray(join.getForeignKey(), join.getForeignKey());
+            StringUtil.toUpperCaseArray(join.getPrimaryKey(), join.getPrimaryKey());
+            // primary key
+            String[] pks = join.getPrimaryKey();
+            TblColRef[] pkCols = new TblColRef[pks.length];
+            for (int i = 0; i < pks.length; i++) {
+                ColumnDesc col = dimTable.findColumnByName(pks[i]);
+                if (col == null) {
+                    throw new IllegalStateException("Can't find column " + pks[i] + " in table " + dimTable.getIdentity());
+                }
+                TblColRef colRef = new TblColRef(col);
+                pks[i] = colRef.getName();
+                pkCols[i] = colRef;
+            }
+            join.setPrimaryKeyColumns(pkCols);
+            // foreign key
+            TableDesc factTable = tables.get(this.factTable.toUpperCase());
+            if (factTable == null) {
+                throw new IllegalStateException("Fact table does not exist:" + this.getFactTable());
+            }
+            String[] fks = join.getForeignKey();
+            TblColRef[] fkCols = new TblColRef[fks.length];
+            for (int i = 0; i < fks.length; i++) {
+                ColumnDesc col = factTable.findColumnByName(fks[i]);
+                if (col == null) {
+                    throw new IllegalStateException("Can't find column " + fks[i] + " in table " + this.getFactTable());
+                }
+                TblColRef colRef = new TblColRef(col);
+                fks[i] = colRef.getName();
+                fkCols[i] = colRef;
+            }
+            join.setForeignKeyColumns(fkCols);
+            // Validate join in dimension
+            if (pkCols.length != fkCols.length) {
+                throw new IllegalStateException("Primary keys(" + lookup.getTable() + ")" + Arrays.toString(pks) + " are not consistent with Foreign keys(" + this.getFactTable() + ") " + Arrays.toString(fks));
+            }
+            for (int i = 0; i < fkCols.length; i++) {
+                if (!fkCols[i].getDatatype().equals(pkCols[i].getDatatype())) {
+                    throw new IllegalStateException("Primary key " + lookup.getTable() + "." + pkCols[i].getName() + "." + pkCols[i].getDatatype() + " are not consistent with Foreign key " + this.getFactTable() + "." + fkCols[i].getName() + "." + fkCols[i].getDatatype());
+                }
+            }
+
+        }
+    }
+
+    /**
+     * Add error info and thrown exception out
+     *
+     * @param message
+     */
+    public void addError(String message) {
+        addError(message, false);
+    }
+
+    /**
+     * @param message
+     *            error message
+     * @param silent
+     *            if throw exception
+     */
+    public void addError(String message, boolean silent) {
+        if (!silent) {
+            throw new IllegalStateException(message);
+        } else {
+            this.errors.add(message);
+        }
+    }
+
+    public List<String> getError() {
+        return this.errors;
+    }
+    @Override
+    public boolean equals(Object o) {
+        if (this == o)
+            return true;
+        if (o == null || getClass() != o.getClass())
+            return false;
+
+        DataModelDesc modelDesc = (DataModelDesc) o;
+
+        if (!name.equals(modelDesc.name))
+            return false;
+        if (!getFactTable().equals(modelDesc.getFactTable()))
+            return false;
+
+        return true;
+    }
+
+
+    @Override
+    public int hashCode() {
+        int result = 0;
+        result = 31 * result + name.hashCode();
+        result = 31 * result + getFactTable().hashCode();
+        return result;
+    }
+
+    @Override
+    public String toString() {
+        return "DataModelDesc [name=" + name + "]";
+    }
+
+    public String getResourcePath() {
+        return concatResourcePath(name);
+    }
+
+    public static String concatResourcePath(String descName) {
+        return ResourceStore.DATA_MODEL_DESC_RESOURCE_ROOT + "/" + descName + MetadataConstants.FILE_SURFIX;
+    }
+
+    public List<DimensionDesc> getDimensions() {
+        return dimensions;
+    }
+
+    public String[] getMetrics() {
+        return metrics;
+    }
+}

http://git-wip-us.apache.org/repos/asf/incubator-kylin/blob/7e8896ac/core-metadata/src/main/java/org/apache/kylin/metadata/model/DataType.java
----------------------------------------------------------------------
diff --git a/core-metadata/src/main/java/org/apache/kylin/metadata/model/DataType.java b/core-metadata/src/main/java/org/apache/kylin/metadata/model/DataType.java
new file mode 100644
index 0000000..c1b1d23
--- /dev/null
+++ b/core-metadata/src/main/java/org/apache/kylin/metadata/model/DataType.java
@@ -0,0 +1,306 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you 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 org.apache.kylin.metadata.model;
+
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.Map;
+import java.util.Set;
+import java.util.concurrent.ConcurrentHashMap;
+import java.util.concurrent.ConcurrentMap;
+import java.util.regex.Matcher;
+import java.util.regex.Pattern;
+
+/**
+ * @author yangli9
+ * 
+ */
+public class DataType {
+    
+    public static final String VALID_TYPES_STRING = "any|char|varchar|boolean|binary" //
+            + "|integer|tinyint|smallint|bigint|decimal|numeric|float|real|double" //
+            + "|date|time|datetime|timestamp|byte|int|short|long|string|hllc" //
+            + "|" + TblColRef.InnerDataTypeEnum.LITERAL.getDataType() //
+            + "|" + TblColRef.InnerDataTypeEnum.DERIVED.getDataType();
+
+    private static final Pattern TYPE_PATTERN = Pattern.compile(
+    // standard sql types, ref:
+    // http://www.w3schools.com/sql/sql_datatypes_general.asp
+            "(" + VALID_TYPES_STRING + ")" + "\\s*" //
+                    + "(?:" + "[(]" + "([\\d\\s,]+)" + "[)]" + ")?", Pattern.CASE_INSENSITIVE);
+
+    public static final Set<String> INTEGER_FAMILY = new HashSet<String>();
+    public static final Set<String> NUMBER_FAMILY = new HashSet<String>();
+    public static final Set<String> DATETIME_FAMILY = new HashSet<String>();
+    public static final Set<String> STRING_FAMILY = new HashSet<String>();
+    private static final Set<Integer> HLLC_PRECISIONS = new HashSet<Integer>();
+    private static final Map<String, String> LEGACY_TYPE_MAP = new HashMap<String, String>();
+    static {
+        INTEGER_FAMILY.add("tinyint");
+        INTEGER_FAMILY.add("smallint");
+        INTEGER_FAMILY.add("integer");
+        INTEGER_FAMILY.add("bigint");
+
+        NUMBER_FAMILY.addAll(INTEGER_FAMILY);
+        NUMBER_FAMILY.add("float");
+        NUMBER_FAMILY.add("double");
+        NUMBER_FAMILY.add("decimal");
+        NUMBER_FAMILY.add("real");
+        NUMBER_FAMILY.add("numeric");
+
+        DATETIME_FAMILY.add("date");
+        DATETIME_FAMILY.add("time");
+        DATETIME_FAMILY.add("datetime");
+        DATETIME_FAMILY.add("timestamp");
+
+        STRING_FAMILY.add("varchar");
+        STRING_FAMILY.add("char");
+
+        LEGACY_TYPE_MAP.put("byte", "tinyint");
+        LEGACY_TYPE_MAP.put("int", "integer");
+        LEGACY_TYPE_MAP.put("short", "smallint");
+        LEGACY_TYPE_MAP.put("long", "bigint");
+        LEGACY_TYPE_MAP.put("string", "varchar");
+        LEGACY_TYPE_MAP.put("hllc10", "hllc(10)");
+        LEGACY_TYPE_MAP.put("hllc12", "hllc(12)");
+        LEGACY_TYPE_MAP.put("hllc14", "hllc(14)");
+        LEGACY_TYPE_MAP.put("hllc15", "hllc(15)");
+        LEGACY_TYPE_MAP.put("hllc16", "hllc(16)");
+
+        for (int i = 10; i <= 16; i++)
+            HLLC_PRECISIONS.add(i);
+    }
+
+    private static final ConcurrentMap<DataType, DataType> CACHE = new ConcurrentHashMap<DataType, DataType>();
+    
+    public static final DataType ANY = DataType.getInstance("any");
+
+    public static DataType getInstance(String type) {
+        if (type == null)
+            return null;
+
+        DataType dataType = new DataType(type);
+        DataType cached = CACHE.get(dataType);
+        if (cached == null) {
+            CACHE.put(dataType, dataType);
+            cached = dataType;
+        }
+        return cached;
+    }
+
+    // ============================================================================
+
+    private String name;
+    private int precision;
+    private int scale;
+
+    DataType(String datatype) {
+        parseDataType(datatype);
+    }
+
+    private void parseDataType(String datatype) {
+        datatype = datatype.trim().toLowerCase();
+        datatype = replaceLegacy(datatype);
+
+        Matcher m = TYPE_PATTERN.matcher(datatype);
+        if (m.matches() == false)
+            throw new IllegalArgumentException("bad data type -- " + datatype + ", does not match " + TYPE_PATTERN);
+
+        name = replaceLegacy(m.group(1));
+        precision = -1;
+        scale = -1;
+
+        String leftover = m.group(2);
+        if (leftover != null) {
+            String[] parts = leftover.split("\\s*,\\s*");
+            for (int i = 0; i < parts.length; i++) {
+                int n;
+                try {
+                    n = Integer.parseInt(parts[i]);
+                } catch (NumberFormatException e) {
+                    throw new IllegalArgumentException("bad data type -- " + datatype + ", precision/scale not numeric");
+                }
+                if (i == 0)
+                    precision = n;
+                else if (i == 1)
+                    scale = n;
+                else
+                    throw new IllegalArgumentException("bad data type -- " + datatype + ", too many precision/scale parts");
+            }
+        }
+
+        // FIXME 256 for unknown string precision
+        if ((name.equals("char") || name.equals("varchar")) && precision == -1) {
+            precision = 256; // to save memory at frontend, e.g. tableau will
+                             // allocate memory according to this
+        }
+
+        // FIXME (19,4) for unknown decimal precision
+        if ((name.equals("decimal") || name.equals("numeric")) && precision == -1) {
+            precision = 19;
+            scale = 4;
+        }
+
+        if (isHLLC() && HLLC_PRECISIONS.contains(precision) == false)
+            throw new IllegalArgumentException("HLLC precision must be one of " + HLLC_PRECISIONS);
+    }
+
+    private String replaceLegacy(String str) {
+        String replace = LEGACY_TYPE_MAP.get(str);
+        return replace == null ? str : replace;
+    }
+
+    public int getSpaceEstimate() {
+        if (isTinyInt()) {
+            return 1;
+        } else if (isSmallInt()) {
+            return 2;
+        } else if (isInt()) {
+            return 4;
+        } else if (isBigInt()) {
+            return 8;
+        } else if (isFloat()) {
+            return 4;
+        } else if (isDouble()) {
+            return 8;
+        } else if (isDecimal()) {
+            return 8;
+        } else if (isHLLC()) {
+            return 1 << precision;
+        }
+        throw new IllegalStateException("The return type : " + name + " is not recognized;");
+    }
+
+    public boolean isStringFamily() {
+        return STRING_FAMILY.contains(name);
+    }
+
+    public boolean isIntegerFamily() {
+        return INTEGER_FAMILY.contains(name);
+    }
+
+    public boolean isNumberFamily() {
+        return NUMBER_FAMILY.contains(name);
+    }
+
+    public boolean isDateTimeFamily() {
+        return DATETIME_FAMILY.contains(name);
+    }
+
+    public boolean isDate() {
+        return name.equals("date");
+    }
+
+    public boolean isTime() {
+        return name.equals("time");
+    }
+
+    public boolean isTimestamp() {
+        return name.equals("timestamp");
+    }
+
+    public boolean isDatetime() {
+        return name.equals("datetime");
+    }
+
+    public boolean isTinyInt() {
+        return name.equals("tinyint");
+    }
+
+    public boolean isSmallInt() {
+        return name.equals("smallint");
+    }
+
+    public boolean isInt() {
+        return name.equals("integer");
+    }
+
+    public boolean isBigInt() {
+        return name.equals("bigint");
+    }
+
+    public boolean isFloat() {
+        return name.equals("float");
+    }
+
+    public boolean isDouble() {
+        return name.equals("double");
+    }
+
+    public boolean isDecimal() {
+        return name.equals("decimal");
+    }
+
+    public boolean isHLLC() {
+        return name.equals("hllc");
+    }
+
+    public String getName() {
+        return name;
+    }
+
+    public int getPrecision() {
+        return precision;
+    }
+
+    public int getScale() {
+        return scale;
+    }
+
+    @Override
+    public int hashCode() {
+        final int prime = 31;
+        int result = 1;
+        result = prime * result + ((name == null) ? 0 : name.hashCode());
+        result = prime * result + precision;
+        result = prime * result + scale;
+        return result;
+    }
+
+    @Override
+    public boolean equals(Object obj) {
+        if (this == obj)
+            return true;
+        if (obj == null)
+            return false;
+        if (getClass() != obj.getClass())
+            return false;
+        DataType other = (DataType) obj;
+        if (name == null) {
+            if (other.name != null)
+                return false;
+        } else if (!name.equals(other.name))
+            return false;
+        if (precision != other.precision)
+            return false;
+        if (scale != other.scale)
+            return false;
+        return true;
+    }
+
+    @Override
+    public String toString() {
+        if (precision < 0 && scale < 0)
+            return name;
+        else if (scale < 0)
+            return name + "(" + precision + ")";
+        else
+            return name + "(" + precision + "," + scale + ")";
+    }
+}

http://git-wip-us.apache.org/repos/asf/incubator-kylin/blob/7e8896ac/core-metadata/src/main/java/org/apache/kylin/metadata/model/DatabaseDesc.java
----------------------------------------------------------------------
diff --git a/core-metadata/src/main/java/org/apache/kylin/metadata/model/DatabaseDesc.java b/core-metadata/src/main/java/org/apache/kylin/metadata/model/DatabaseDesc.java
new file mode 100644
index 0000000..3269e43
--- /dev/null
+++ b/core-metadata/src/main/java/org/apache/kylin/metadata/model/DatabaseDesc.java
@@ -0,0 +1,78 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you 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 org.apache.kylin.metadata.model;
+
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.List;
+import java.util.Set;
+
+/**
+ * @author xjiang
+ */
+public class DatabaseDesc {
+    private String name;
+
+    /**
+     * @return the name
+     */
+    public String getName() {
+        return name == null ? "null" : name.toUpperCase();
+    }
+
+    /**
+     * @param name
+     *            the name to set
+     */
+    public void setName(String name) {
+        this.name = name;
+    }
+
+    /*
+     * (non-Javadoc)
+     * 
+     * @see java.lang.Object#toString()
+     */
+    @Override
+    public String toString() {
+
+        return "DatabaseDesc [name=" + name + "]";
+    }
+
+    public static HashMap<String, Integer> extractDatabaseOccurenceCounts(Set<TableDesc> tables) {
+        HashMap<String, Integer> databaseCounts = new HashMap<String, Integer>();
+        for (TableDesc tableDesc : tables) {
+            String databaseName = tableDesc.getDatabase();
+            Integer counter = databaseCounts.get(databaseName);
+            if (counter != null)
+                databaseCounts.put(databaseName, counter + 1);
+            else
+                databaseCounts.put(databaseName, 1);
+        }
+        return databaseCounts;
+    }
+
+    public static HashSet<String> extractDatabaseNames(List<TableDesc> tables) {
+        HashSet<String> databaseNames = new HashSet<String>();
+        for (TableDesc tableDesc : tables) {
+            databaseNames.add(tableDesc.getDatabase());
+        }
+        return databaseNames;
+    }
+}

http://git-wip-us.apache.org/repos/asf/incubator-kylin/blob/7e8896ac/core-metadata/src/main/java/org/apache/kylin/metadata/model/DimensionDesc.java
----------------------------------------------------------------------
diff --git a/core-metadata/src/main/java/org/apache/kylin/metadata/model/DimensionDesc.java b/core-metadata/src/main/java/org/apache/kylin/metadata/model/DimensionDesc.java
new file mode 100644
index 0000000..73e71b6
--- /dev/null
+++ b/core-metadata/src/main/java/org/apache/kylin/metadata/model/DimensionDesc.java
@@ -0,0 +1,73 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you 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 org.apache.kylin.metadata.model;
+
+import com.fasterxml.jackson.annotation.JsonAutoDetect;
+import com.fasterxml.jackson.annotation.JsonProperty;
+import org.apache.kylin.common.util.StringUtil;
+
+import java.util.List;
+
+/**
+ */
+@JsonAutoDetect(fieldVisibility = JsonAutoDetect.Visibility.NONE, getterVisibility = JsonAutoDetect.Visibility.NONE, isGetterVisibility = JsonAutoDetect.Visibility.NONE, setterVisibility = JsonAutoDetect.Visibility.NONE)
+public class DimensionDesc {
+    @JsonProperty("table")
+    private String table;
+    @JsonProperty("columns")
+    private String[] columns;
+
+    public String getTable() {
+        return table;
+    }
+
+    public void setTable(String table) {
+        this.table = table;
+    }
+
+    public String[] getColumns() {
+        return columns;
+    }
+
+    public void setColumns(String[] columns) {
+        this.columns = columns;
+    }
+
+    public static void capicalizeStrings(List<DimensionDesc> dimensions) {
+        if (dimensions != null) {
+            for (DimensionDesc dimensionDesc : dimensions) {
+                dimensionDesc.setTable(dimensionDesc.getTable().toUpperCase());
+                if (dimensionDesc.getColumns() != null) {
+                    StringUtil.toUpperCaseArray(dimensionDesc.getColumns(), dimensionDesc.getColumns());
+                }
+            }
+        }
+    }
+
+    public static int getColumnCount(List<DimensionDesc> dimensionDescs) {
+        int count = 0;
+        for (DimensionDesc dimensionDesc : dimensionDescs) {
+            if (dimensionDesc.getColumns() != null) {
+                count += dimensionDesc.getColumns().length;
+            }
+        }
+        return count;
+    }
+
+}

http://git-wip-us.apache.org/repos/asf/incubator-kylin/blob/7e8896ac/core-metadata/src/main/java/org/apache/kylin/metadata/model/FunctionDesc.java
----------------------------------------------------------------------
diff --git a/core-metadata/src/main/java/org/apache/kylin/metadata/model/FunctionDesc.java b/core-metadata/src/main/java/org/apache/kylin/metadata/model/FunctionDesc.java
new file mode 100644
index 0000000..af125e2
--- /dev/null
+++ b/core-metadata/src/main/java/org/apache/kylin/metadata/model/FunctionDesc.java
@@ -0,0 +1,223 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you 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 org.apache.kylin.metadata.model;
+
+import com.fasterxml.jackson.annotation.JsonAutoDetect;
+import com.fasterxml.jackson.annotation.JsonAutoDetect.Visibility;
+import com.fasterxml.jackson.annotation.JsonProperty;
+
+import java.util.Collection;
+
+/**
+ */
+@JsonAutoDetect(fieldVisibility = Visibility.NONE, getterVisibility = Visibility.NONE, isGetterVisibility = Visibility.NONE, setterVisibility = Visibility.NONE)
+public class FunctionDesc {
+
+    public static final String FUNC_SUM = "SUM";
+    public static final String FUNC_MIN = "MIN";
+    public static final String FUNC_MAX = "MAX";
+    public static final String FUNC_COUNT = "COUNT";
+    public static final String FUNC_COUNT_DISTINCT = "COUNT_DISTINCT";
+
+    public static final String PARAMTER_TYPE_CONSTANT = "constant";
+    public static final String PARAMETER_TYPE_COLUMN = "column";
+
+    @JsonProperty("expression")
+    private String expression;
+    @JsonProperty("parameter")
+    private ParameterDesc parameter;
+    @JsonProperty("returntype")
+    private String returnType;
+
+    private DataType returnDataType;
+    private boolean isDimensionAsMetric = false;
+
+    public String getRewriteFieldName() {
+        if (isSum()) {
+            return getParameter().getValue();
+        } else if (isCount()) {
+            return "COUNT__"; // ignores parameter, count(*), count(1), count(col) are all the same
+        } else {
+            return getFullExpression().replaceAll("[(), ]", "_");
+        }
+    }
+
+    public boolean needRewrite() {
+        return !isSum() && !isDimensionAsMetric();
+    }
+
+    public ColumnDesc newFakeRewriteColumn(TableDesc sourceTable) {
+        ColumnDesc fakeCol = new ColumnDesc();
+        fakeCol.setName(getRewriteFieldName());
+        fakeCol.setDatatype(getSQLType().toString());
+        if (isCount())
+            fakeCol.setNullable(false);
+        fakeCol.init(sourceTable);
+        return fakeCol;
+    }
+
+    public boolean isMin() {
+        return FUNC_MIN.equalsIgnoreCase(expression);
+    }
+
+    public boolean isMax() {
+        return FUNC_MAX.equalsIgnoreCase(expression);
+    }
+
+    public boolean isSum() {
+        return FUNC_SUM.equalsIgnoreCase(expression);
+    }
+
+    public boolean isCount() {
+        return FUNC_COUNT.equalsIgnoreCase(expression);
+    }
+
+    public boolean isCountDistinct() {
+        return FUNC_COUNT_DISTINCT.equalsIgnoreCase(expression);
+    }
+
+    public boolean isHolisticCountDistinct() {
+        if (isCountDistinct() && returnDataType != null && returnDataType.isBigInt()) {
+            return true;
+        } else {
+            return false;
+        }
+    }
+
+    /**
+     * Get Full Expression such as sum(amount), count(1), count(*)...
+     */
+    public String getFullExpression() {
+        StringBuilder sb = new StringBuilder(expression);
+        sb.append("(");
+        if (parameter != null) {
+            sb.append(parameter.getValue());
+        }
+        sb.append(")");
+        return sb.toString();
+    }
+
+    public boolean isDimensionAsMetric() {
+        return isDimensionAsMetric;
+    }
+
+    public void setDimensionAsMetric(boolean isDimensionAsMetric) {
+        this.isDimensionAsMetric = isDimensionAsMetric;
+    }
+
+    public String getExpression() {
+        return expression;
+    }
+
+    public void setExpression(String expression) {
+        this.expression = expression;
+    }
+
+    public ParameterDesc getParameter() {
+        return parameter;
+    }
+
+    public void setParameter(ParameterDesc parameter) {
+        this.parameter = parameter;
+    }
+
+    public DataType getSQLType() {
+        if (isCountDistinct())
+            return DataType.ANY;
+        else if (isSum() || isMax() || isMin())
+            return parameter.getColRefs().get(0).getType();
+        else
+            return returnDataType;
+    }
+
+    public String getReturnType() {
+        return returnType;
+    }
+
+    public DataType getReturnDataType() {
+        return returnDataType;
+    }
+
+    public void setReturnType(String returnType) {
+        this.returnType = returnType;
+        this.initReturnDataType();
+    }
+    
+    // Jackson does not provide object post-processing currently
+    public void initReturnDataType() {
+        this.returnDataType = DataType.getInstance(returnType);
+    }
+
+    public TblColRef selectTblColRef(Collection<TblColRef> metricColumns, String factTableName) {
+        if (this.isCount())
+            return null; // count is not about any column but the whole row
+
+        ParameterDesc parameter = this.getParameter();
+        if (parameter == null)
+            return null;
+
+        String columnName = parameter.getValue();
+        for (TblColRef col : metricColumns) {
+            if (col.isSameAs(factTableName, columnName)) {
+                return col;
+            }
+        }
+        return null;
+    }
+
+    @Override
+    public int hashCode() {
+        final int prime = 31;
+        int result = 1;
+        result = prime * result + ((expression == null) ? 0 : expression.hashCode());
+        result = prime * result + ((isCount() || parameter == null) ? 0 : parameter.hashCode());
+        return result;
+    }
+
+    @Override
+    public boolean equals(Object obj) {
+        if (this == obj)
+            return true;
+        if (obj == null)
+            return false;
+        if (getClass() != obj.getClass())
+            return false;
+        FunctionDesc other = (FunctionDesc) obj;
+        if (expression == null) {
+            if (other.expression != null)
+                return false;
+        } else if (!expression.equals(other.expression))
+            return false;
+        // NOTE: don't check the parameter of count()
+        if (isCount() == false) {
+            if (parameter == null) {
+                if (other.parameter != null)
+                    return false;
+            } else if (!parameter.equals(other.parameter))
+                return false;
+        }
+        return true;
+    }
+
+    @Override
+    public String toString() {
+        return "FunctionDesc [expression=" + expression + ", parameter=" + parameter + ", returnType=" + returnType + "]";
+    }
+
+}

http://git-wip-us.apache.org/repos/asf/incubator-kylin/blob/7e8896ac/core-metadata/src/main/java/org/apache/kylin/metadata/model/IBuildable.java
----------------------------------------------------------------------
diff --git a/core-metadata/src/main/java/org/apache/kylin/metadata/model/IBuildable.java b/core-metadata/src/main/java/org/apache/kylin/metadata/model/IBuildable.java
new file mode 100644
index 0000000..e6175fd
--- /dev/null
+++ b/core-metadata/src/main/java/org/apache/kylin/metadata/model/IBuildable.java
@@ -0,0 +1,28 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you 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 org.apache.kylin.metadata.model;
+
+public interface IBuildable {
+
+    public int getSourceType();
+    
+    public int getEngineType();
+    
+    public int getStorageType();
+}

http://git-wip-us.apache.org/repos/asf/incubator-kylin/blob/7e8896ac/core-metadata/src/main/java/org/apache/kylin/metadata/model/IJoinedFlatTableDesc.java
----------------------------------------------------------------------
diff --git a/core-metadata/src/main/java/org/apache/kylin/metadata/model/IJoinedFlatTableDesc.java b/core-metadata/src/main/java/org/apache/kylin/metadata/model/IJoinedFlatTableDesc.java
new file mode 100644
index 0000000..0124b9b
--- /dev/null
+++ b/core-metadata/src/main/java/org/apache/kylin/metadata/model/IJoinedFlatTableDesc.java
@@ -0,0 +1,35 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you 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 org.apache.kylin.metadata.model;
+
+import java.util.List;
+
+/**
+ */
+public interface IJoinedFlatTableDesc {
+
+    public String getTableName();
+
+    public List<IntermediateColumnDesc> getColumnList();
+
+    public DataModelDesc getDataModel();
+
+    public DataModelDesc.RealizationCapacity getCapacity();
+
+}

http://git-wip-us.apache.org/repos/asf/incubator-kylin/blob/7e8896ac/core-metadata/src/main/java/org/apache/kylin/metadata/model/IntermediateColumnDesc.java
----------------------------------------------------------------------
diff --git a/core-metadata/src/main/java/org/apache/kylin/metadata/model/IntermediateColumnDesc.java b/core-metadata/src/main/java/org/apache/kylin/metadata/model/IntermediateColumnDesc.java
new file mode 100644
index 0000000..9dbe63e
--- /dev/null
+++ b/core-metadata/src/main/java/org/apache/kylin/metadata/model/IntermediateColumnDesc.java
@@ -0,0 +1,62 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you 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 org.apache.kylin.metadata.model;
+
+import org.apache.kylin.metadata.model.TblColRef;
+
+/**
+ */
+public class IntermediateColumnDesc {
+    private String id;
+    private TblColRef colRef;
+
+    public IntermediateColumnDesc(String id, TblColRef colRef) {
+        this.id = id;
+        this.colRef = colRef;
+    }
+
+    public String getId() {
+        return id;
+    }
+
+    public TblColRef getColRef() {
+        return this.colRef;
+    }
+
+    public String getColumnName() {
+        return colRef.getName();
+    }
+
+    public String getDataType() {
+        return colRef.getDatatype();
+    }
+
+    public String getTableName() {
+        return colRef.getTable();
+    }
+
+    public boolean isSameAs(String tableName, String columnName) {
+        return colRef.isSameAs(tableName, columnName);
+    }
+
+    public String getCanonicalName() {
+        return colRef.getCanonicalName();
+    }
+
+}

http://git-wip-us.apache.org/repos/asf/incubator-kylin/blob/7e8896ac/core-metadata/src/main/java/org/apache/kylin/metadata/model/JoinDesc.java
----------------------------------------------------------------------
diff --git a/core-metadata/src/main/java/org/apache/kylin/metadata/model/JoinDesc.java b/core-metadata/src/main/java/org/apache/kylin/metadata/model/JoinDesc.java
new file mode 100644
index 0000000..da907b9
--- /dev/null
+++ b/core-metadata/src/main/java/org/apache/kylin/metadata/model/JoinDesc.java
@@ -0,0 +1,135 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you 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 org.apache.kylin.metadata.model;
+
+import com.fasterxml.jackson.annotation.JsonAutoDetect;
+import com.fasterxml.jackson.annotation.JsonAutoDetect.Visibility;
+import com.fasterxml.jackson.annotation.JsonProperty;
+
+import java.util.Arrays;
+
+/**
+ */
+@JsonAutoDetect(fieldVisibility = Visibility.NONE, getterVisibility = Visibility.NONE, isGetterVisibility = Visibility.NONE, setterVisibility = Visibility.NONE)
+public class JoinDesc {
+
+    // inner, left, right, outer...
+    @JsonProperty("type")
+    private String type;
+    @JsonProperty("primary_key")
+    private String[] primaryKey;
+    @JsonProperty("foreign_key")
+    private String[] foreignKey;
+
+    private TblColRef[] primaryKeyColumns;
+    private TblColRef[] foreignKeyColumns;
+
+    public void swapPKFK() {
+        String[] t = primaryKey;
+        primaryKey = foreignKey;
+        foreignKey = t;
+
+        TblColRef[] tt = primaryKeyColumns;
+        primaryKeyColumns = foreignKeyColumns;
+        foreignKeyColumns = tt;
+    }
+
+    public String getType() {
+        return type;
+    }
+
+    public void setType(String type) {
+        this.type = type;
+    }
+
+    public String[] getPrimaryKey() {
+        return primaryKey;
+    }
+
+    public void setPrimaryKey(String[] primaryKey) {
+        this.primaryKey = primaryKey;
+    }
+
+    public String[] getForeignKey() {
+        return foreignKey;
+    }
+
+    public void setForeignKey(String[] foreignKey) {
+        this.foreignKey = foreignKey;
+    }
+
+    public TblColRef[] getPrimaryKeyColumns() {
+        return primaryKeyColumns;
+    }
+
+    public void setPrimaryKeyColumns(TblColRef[] primaryKeyColumns) {
+        this.primaryKeyColumns = primaryKeyColumns;
+    }
+
+    public TblColRef[] getForeignKeyColumns() {
+        return foreignKeyColumns;
+    }
+
+    public void setForeignKeyColumns(TblColRef[] foreignKeyColumns) {
+        this.foreignKeyColumns = foreignKeyColumns;
+    }
+
+    @Override
+    public int hashCode() {
+        final int prime = 31;
+        int result = 1;
+        result = prime * result + Arrays.hashCode(primaryKeyColumns);
+        result = prime * result + Arrays.hashCode(foreignKeyColumns);
+        result = prime * result + this.type.hashCode();
+        return result;
+    }
+
+    @Override
+    public boolean equals(Object obj) {
+        if (this == obj)
+            return true;
+        if (obj == null)
+            return false;
+        if (getClass() != obj.getClass())
+            return false;
+        JoinDesc other = (JoinDesc) obj;
+
+        if (!this.columnsEqualIgnoringOrder(foreignKeyColumns, other.foreignKeyColumns))
+            return false;
+        if (!this.columnsEqualIgnoringOrder(primaryKeyColumns, other.primaryKeyColumns))
+            return false;
+
+        if (!this.type.equalsIgnoreCase(other.getType()))
+            return false;
+        return true;
+    }
+
+    private boolean columnsEqualIgnoringOrder(TblColRef[] a, TblColRef[] b) {
+        if (a.length != b.length)
+            return false;
+
+        return Arrays.asList(a).containsAll(Arrays.asList(b));
+    }
+
+    @Override
+    public String toString() {
+        return "JoinDesc [type=" + type + ", primary_key=" + Arrays.toString(primaryKey) + ", foreign_key=" + Arrays.toString(foreignKey) + "]";
+    }
+
+}

http://git-wip-us.apache.org/repos/asf/incubator-kylin/blob/7e8896ac/core-metadata/src/main/java/org/apache/kylin/metadata/model/LookupDesc.java
----------------------------------------------------------------------
diff --git a/core-metadata/src/main/java/org/apache/kylin/metadata/model/LookupDesc.java b/core-metadata/src/main/java/org/apache/kylin/metadata/model/LookupDesc.java
new file mode 100644
index 0000000..1904c0a
--- /dev/null
+++ b/core-metadata/src/main/java/org/apache/kylin/metadata/model/LookupDesc.java
@@ -0,0 +1,50 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you 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 org.apache.kylin.metadata.model;
+
+import com.fasterxml.jackson.annotation.JsonAutoDetect;
+import com.fasterxml.jackson.annotation.JsonProperty;
+import com.fasterxml.jackson.annotation.JsonAutoDetect.Visibility;
+
+@JsonAutoDetect(fieldVisibility = Visibility.NONE, getterVisibility = Visibility.NONE, isGetterVisibility = Visibility.NONE, setterVisibility = Visibility.NONE)
+public class LookupDesc {
+
+    @JsonProperty("table")
+    private String table;
+
+    @JsonProperty("join")
+    private JoinDesc join;
+
+    public String getTable() {
+        return table;
+    }
+
+    public void setTable(String table) {
+        this.table = table;
+    }
+
+    public JoinDesc getJoin() {
+        return join;
+    }
+
+    public void setJoin(JoinDesc join) {
+        this.join = join;
+    }
+
+}

http://git-wip-us.apache.org/repos/asf/incubator-kylin/blob/7e8896ac/core-metadata/src/main/java/org/apache/kylin/metadata/model/MeasureDesc.java
----------------------------------------------------------------------
diff --git a/core-metadata/src/main/java/org/apache/kylin/metadata/model/MeasureDesc.java b/core-metadata/src/main/java/org/apache/kylin/metadata/model/MeasureDesc.java
new file mode 100644
index 0000000..7237588
--- /dev/null
+++ b/core-metadata/src/main/java/org/apache/kylin/metadata/model/MeasureDesc.java
@@ -0,0 +1,104 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you 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 org.apache.kylin.metadata.model;
+
+import com.fasterxml.jackson.annotation.JsonAutoDetect;
+import com.fasterxml.jackson.annotation.JsonAutoDetect.Visibility;
+import com.fasterxml.jackson.annotation.JsonProperty;
+
+/**
+ */
+
+@JsonAutoDetect(fieldVisibility = Visibility.NONE, getterVisibility = Visibility.NONE, isGetterVisibility = Visibility.NONE, setterVisibility = Visibility.NONE)
+public class MeasureDesc {
+
+    @JsonProperty("id")
+    private int id;
+    @JsonProperty("name")
+    private String name;
+    @JsonProperty("function")
+    private FunctionDesc function;
+    @JsonProperty("dependent_measure_ref")
+    private String dependentMeasureRef;
+
+    public int getId() {
+        return id;
+    }
+
+    public void setId(int id) {
+        this.id = id;
+    }
+
+    public String getName() {
+        return name;
+    }
+
+    public void setName(String name) {
+        this.name = name;
+    }
+
+    public FunctionDesc getFunction() {
+        return function;
+    }
+
+    public void setFunction(FunctionDesc function) {
+        this.function = function;
+    }
+
+    public String getDependentMeasureRef() {
+        return dependentMeasureRef;
+    }
+
+    public void setDependentMeasureRef(String dependentMeasureRef) {
+        this.dependentMeasureRef = dependentMeasureRef;
+    }
+
+    @Override
+    public boolean equals(Object o) {
+        if (this == o)
+            return true;
+        if (o == null || getClass() != o.getClass())
+            return false;
+
+        MeasureDesc that = (MeasureDesc) o;
+
+        if (!function.equals(that.getFunction()))
+            return false;
+
+        if (dependentMeasureRef != null && that.getDependentMeasureRef() == null
+                || dependentMeasureRef == null && that.getDependentMeasureRef() != null)
+            return false;
+
+        if (dependentMeasureRef == null && that.getDependentMeasureRef() == null)
+            return true;
+
+        return dependentMeasureRef.equalsIgnoreCase(that.getDependentMeasureRef());
+    }
+
+    @Override
+    public int hashCode() {
+        return id;
+    }
+
+    @Override
+    public String toString() {
+        return "MeasureDesc [name=" + name + ", function=" + function + "]";
+    }
+
+}

http://git-wip-us.apache.org/repos/asf/incubator-kylin/blob/7e8896ac/core-metadata/src/main/java/org/apache/kylin/metadata/model/ParameterDesc.java
----------------------------------------------------------------------
diff --git a/core-metadata/src/main/java/org/apache/kylin/metadata/model/ParameterDesc.java b/core-metadata/src/main/java/org/apache/kylin/metadata/model/ParameterDesc.java
new file mode 100644
index 0000000..14a073f
--- /dev/null
+++ b/core-metadata/src/main/java/org/apache/kylin/metadata/model/ParameterDesc.java
@@ -0,0 +1,122 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you 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 org.apache.kylin.metadata.model;
+
+import com.fasterxml.jackson.annotation.JsonAutoDetect;
+import com.fasterxml.jackson.annotation.JsonAutoDetect.Visibility;
+import com.fasterxml.jackson.annotation.JsonProperty;
+import org.apache.hadoop.util.StringUtils;
+
+import java.io.UnsupportedEncodingException;
+import java.util.Arrays;
+import java.util.List;
+
+/**
+ */
+@JsonAutoDetect(fieldVisibility = Visibility.NONE, getterVisibility = Visibility.NONE, isGetterVisibility = Visibility.NONE, setterVisibility = Visibility.NONE)
+public class ParameterDesc {
+
+    public static final String COLUMN_TYPE = "column";
+
+    @JsonProperty("type")
+    private String type;
+    @JsonProperty("value")
+    private String value;
+
+    private List<TblColRef> colRefs;
+
+    public String getType() {
+        return type;
+    }
+
+    public void setType(String type) {
+        this.type = type;
+    }
+
+    public byte[] getBytes() throws UnsupportedEncodingException {
+        return value.getBytes("UTF-8");
+    }
+
+    public String getValue() {
+        return value;
+    }
+
+    public void setValue(String value) {
+        this.value = value;
+    }
+
+    public List<TblColRef> getColRefs() {
+        return colRefs;
+    }
+
+    public void setColRefs(List<TblColRef> colRefs) {
+        this.colRefs = colRefs;
+    }
+
+    public boolean isColumnType() {
+        return COLUMN_TYPE.equals(type);
+    }
+
+    public void normalizeColumnValue() {
+        if (isColumnType()) {
+            String values[] = value.split("\\s*,\\s*");
+            for (int i = 0; i < values.length; i++)
+                values[i] = values[i].toUpperCase();
+            Arrays.sort(values);
+            value = StringUtils.join(",", values);
+        }
+    }
+
+    @Override
+    public int hashCode() {
+        final int prime = 31;
+        int result = 1;
+        result = prime * result + ((type == null) ? 0 : type.hashCode());
+        result = prime * result + ((value == null) ? 0 : value.hashCode());
+        return result;
+    }
+
+    @Override
+    public boolean equals(Object obj) {
+        if (this == obj)
+            return true;
+        if (obj == null)
+            return false;
+        if (getClass() != obj.getClass())
+            return false;
+        ParameterDesc other = (ParameterDesc) obj;
+        if (type == null) {
+            if (other.type != null)
+                return false;
+        } else if (!type.equals(other.type))
+            return false;
+        if (value == null) {
+            if (other.value != null)
+                return false;
+        } else if (!value.equals(other.value))
+            return false;
+        return true;
+    }
+
+    @Override
+    public String toString() {
+        return "ParameterDesc [type=" + type + ", value=" + value + "]";
+    }
+
+}

http://git-wip-us.apache.org/repos/asf/incubator-kylin/blob/7e8896ac/core-metadata/src/main/java/org/apache/kylin/metadata/model/PartitionDesc.java
----------------------------------------------------------------------
diff --git a/core-metadata/src/main/java/org/apache/kylin/metadata/model/PartitionDesc.java b/core-metadata/src/main/java/org/apache/kylin/metadata/model/PartitionDesc.java
new file mode 100644
index 0000000..7ae234c
--- /dev/null
+++ b/core-metadata/src/main/java/org/apache/kylin/metadata/model/PartitionDesc.java
@@ -0,0 +1,188 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you 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 org.apache.kylin.metadata.model;
+
+import java.util.Map;
+
+import org.apache.commons.lang3.StringUtils;
+import org.apache.kylin.common.util.ClassUtil;
+import org.apache.kylin.common.util.DateFormat;
+import org.apache.kylin.common.util.StringSplitter;
+
+import com.fasterxml.jackson.annotation.JsonAutoDetect;
+import com.fasterxml.jackson.annotation.JsonAutoDetect.Visibility;
+import com.fasterxml.jackson.annotation.JsonProperty;
+
+/**
+ * @author xduo
+ * 
+ */
+@JsonAutoDetect(fieldVisibility = Visibility.NONE, getterVisibility = Visibility.NONE, isGetterVisibility = Visibility.NONE, setterVisibility = Visibility.NONE)
+public class PartitionDesc {
+
+    public static enum PartitionType {
+        APPEND, //
+        UPDATE_INSERT // not used since 0.7.1
+    }
+
+    @JsonProperty("partition_date_column")
+    private String partitionDateColumn;
+    @JsonProperty("partition_date_start")
+    private long partitionDateStart = 0L;
+    @JsonProperty("partition_type")
+    private PartitionType partitionType = PartitionType.APPEND;
+    @JsonProperty("partition_condition_builder")
+    private String partitionConditionBuilderClz = DefaultPartitionConditionBuilder.class.getName();
+
+    private TblColRef partitionDateColumnRef;
+    private IPartitionConditionBuilder partitionConditionBuilder;
+
+    public void init(Map<String, TableDesc> tables) {
+        if (StringUtils.isEmpty(partitionDateColumn))
+            return;
+
+        partitionDateColumn = partitionDateColumn.toUpperCase();
+
+        String[] columns = StringSplitter.split(partitionDateColumn, ".");
+
+        if (null != columns && columns.length == 3) {
+            String tableName = columns[0].toUpperCase() + "." + columns[1].toUpperCase();
+
+            TableDesc table = tables.get(tableName);
+            ColumnDesc col = table.findColumnByName(columns[2]);
+            if (col != null) {
+                partitionDateColumnRef = new TblColRef(col);
+            } else {
+                throw new IllegalStateException("The column '" + partitionDateColumn + "' provided in 'partition_date_column' doesn't exist.");
+            }
+        } else {
+            throw new IllegalStateException("The 'partition_date_column' format is invalid: " + partitionDateColumn + ", it should be {db}.{table}.{column}.");
+        }
+
+        partitionConditionBuilder = (IPartitionConditionBuilder) ClassUtil.newInstance(partitionConditionBuilderClz);
+    }
+
+    public boolean isPartitioned() {
+        return partitionDateColumnRef != null;
+    }
+
+    public String getPartitionDateColumn() {
+        return partitionDateColumn;
+    }
+
+    public void setPartitionDateColumn(String partitionDateColumn) {
+        this.partitionDateColumn = partitionDateColumn;
+    }
+
+    public long getPartitionDateStart() {
+        return partitionDateStart;
+    }
+
+    public void setPartitionDateStart(long partitionDateStart) {
+        this.partitionDateStart = partitionDateStart;
+    }
+
+    public PartitionType getCubePartitionType() {
+        return partitionType;
+    }
+
+    public void setCubePartitionType(PartitionType partitionType) {
+        this.partitionType = partitionType;
+    }
+
+    public IPartitionConditionBuilder getPartitionConditionBuilder() {
+        return partitionConditionBuilder;
+    }
+
+    public TblColRef getPartitionDateColumnRef() {
+        return partitionDateColumnRef;
+    }
+
+    // ============================================================================
+
+    public static interface IPartitionConditionBuilder {
+        String buildDateRangeCondition(PartitionDesc partDesc, long startInclusive, long endExclusive, Map<String, String> tableAlias);
+    }
+
+    public static class DefaultPartitionConditionBuilder implements IPartitionConditionBuilder {
+
+        @Override
+        public String buildDateRangeCondition(PartitionDesc partDesc, long startInclusive, long endExclusive, Map<String, String> tableAlias) {
+            String partitionColumnName = partDesc.getPartitionDateColumn();
+
+            // convert to use table alias
+            int indexOfDot = partitionColumnName.lastIndexOf(".");
+            if (indexOfDot > 0) {
+                String partitionTableName = partitionColumnName.substring(0, indexOfDot);
+                if (tableAlias != null && tableAlias.containsKey(partitionTableName))
+                    partitionColumnName = tableAlias.get(partitionTableName) + partitionColumnName.substring(indexOfDot);
+            }
+
+            StringBuilder builder = new StringBuilder();
+
+            if (startInclusive > 0) {
+                builder.append(partitionColumnName + " >= '" + DateFormat.formatToDateStr(startInclusive) + "' ");
+                builder.append("AND ");
+            }
+            builder.append(partitionColumnName + " < '" + DateFormat.formatToDateStr(endExclusive) + "'");
+
+            return builder.toString();
+        }
+
+    }
+
+
+    /**
+     * Another implementation of IPartitionConditionBuilder, for the fact tables which have three partition columns "YEAR", "MONTH", and "DAY"; This
+     * class will concat the three columns into yyyy-MM-dd format for query hive;
+     */
+    public static class YearMonthDayPartitionConditionBuilder implements PartitionDesc.IPartitionConditionBuilder {
+
+        @Override
+        public String buildDateRangeCondition(PartitionDesc partDesc, long startInclusive, long endExclusive, Map<String, String> tableAlias) {
+
+            String partitionColumnName = partDesc.getPartitionDateColumn();
+            String partitionTableName;
+
+            // convert to use table alias
+            int indexOfDot = partitionColumnName.lastIndexOf(".");
+            if (indexOfDot > 0) {
+                partitionTableName = partitionColumnName.substring(0, indexOfDot).toUpperCase();
+            } else {
+                throw new IllegalStateException("The partitionColumnName is invalid: " + partitionColumnName);
+            }
+
+            if (tableAlias.containsKey(partitionTableName)) {
+                partitionTableName = tableAlias.get(partitionTableName);
+            }
+
+            String concatField = String.format("CONCAT(%s.YEAR,'-',%s.MONTH,'-',%s.DAY)", partitionTableName, partitionTableName, partitionTableName);
+            StringBuilder builder = new StringBuilder();
+
+            if (startInclusive > 0) {
+                builder.append(concatField + " >= '" + DateFormat.formatToDateStr(startInclusive) + "' ");
+                builder.append("AND ");
+            }
+            builder.append(concatField + " < '" + DateFormat.formatToDateStr(endExclusive) + "'");
+
+
+            return builder.toString();
+        }
+    }
+}

http://git-wip-us.apache.org/repos/asf/incubator-kylin/blob/7e8896ac/core-metadata/src/main/java/org/apache/kylin/metadata/model/SegmentStatusEnum.java
----------------------------------------------------------------------
diff --git a/core-metadata/src/main/java/org/apache/kylin/metadata/model/SegmentStatusEnum.java b/core-metadata/src/main/java/org/apache/kylin/metadata/model/SegmentStatusEnum.java
new file mode 100644
index 0000000..65c44df
--- /dev/null
+++ b/core-metadata/src/main/java/org/apache/kylin/metadata/model/SegmentStatusEnum.java
@@ -0,0 +1,27 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you 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 org.apache.kylin.metadata.model;
+
+/**
+ * @author xduo
+ * 
+ */
+public enum SegmentStatusEnum {
+    NEW, READY, READY_PENDING
+}

http://git-wip-us.apache.org/repos/asf/incubator-kylin/blob/7e8896ac/core-metadata/src/main/java/org/apache/kylin/metadata/model/TableDesc.java
----------------------------------------------------------------------
diff --git a/core-metadata/src/main/java/org/apache/kylin/metadata/model/TableDesc.java b/core-metadata/src/main/java/org/apache/kylin/metadata/model/TableDesc.java
new file mode 100644
index 0000000..4bc9df3
--- /dev/null
+++ b/core-metadata/src/main/java/org/apache/kylin/metadata/model/TableDesc.java
@@ -0,0 +1,173 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you 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 org.apache.kylin.metadata.model;
+
+import com.fasterxml.jackson.annotation.JsonAutoDetect;
+import com.fasterxml.jackson.annotation.JsonAutoDetect.Visibility;
+import com.fasterxml.jackson.annotation.JsonProperty;
+import org.apache.kylin.common.persistence.ResourceStore;
+import org.apache.kylin.common.persistence.RootPersistentEntity;
+import org.apache.kylin.common.util.StringSplitter;
+
+import java.util.Arrays;
+import java.util.Comparator;
+
+/**
+ * Table Metadata from Source. All name should be uppercase.
+ */
+@JsonAutoDetect(fieldVisibility = Visibility.NONE, getterVisibility = Visibility.NONE, isGetterVisibility = Visibility.NONE, setterVisibility = Visibility.NONE)
+public class TableDesc extends RootPersistentEntity {
+    @JsonProperty("name")
+    private String name;
+    @JsonProperty("columns")
+    private ColumnDesc[] columns;
+
+    private DatabaseDesc database = new DatabaseDesc();
+
+    private String identity = null;
+
+    public ColumnDesc findColumnByName(String name) {
+        //ignore the db name and table name if exists
+        int lastIndexOfDot = name.lastIndexOf(".");
+        if (lastIndexOfDot >= 0) {
+            name = name.substring(lastIndexOfDot + 1);
+        }
+
+        for (ColumnDesc c : columns) {
+            // return first matched column
+            if (name.equalsIgnoreCase(c.getName())) {
+                return c;
+            }
+        }
+        return null;
+    }
+
+    public String getResourcePath() {
+        return concatResourcePath(getIdentity());
+    }
+
+    /**
+     * @deprecated this is for compatible with data model v1;
+     * @return
+     */
+    public String getResourcePathV1() {
+        return concatResourcePath(name);
+    }
+
+    public String getIdentity() {
+        if (identity == null) {
+            identity = String.format("%s.%s", this.getDatabase().toUpperCase(), this.getName()).toUpperCase();
+        }
+        return identity;
+    }
+
+    public static String concatResourcePath(String tableIdentity) {
+        return ResourceStore.TABLE_RESOURCE_ROOT + "/" + tableIdentity + ".json";
+    }
+
+    public static String concatExdResourcePath(String tableIdentity) {
+        return ResourceStore.TABLE_EXD_RESOURCE_ROOT + "/" + tableIdentity + ".json";
+    }
+
+    // ============================================================================
+
+    public String getName() {
+        return this.name;
+    }
+
+    public void setName(String name) {
+        if (name != null) {
+            String[] splits = StringSplitter.split(name, ".");
+            if (splits.length == 2) {
+                this.setDatabase(splits[0]);
+                this.name = splits[1];
+            } else if (splits.length == 1) {
+                this.name = splits[0];
+            }
+        } else {
+            this.name = name;
+        }
+    }
+
+    @JsonProperty("database")
+    public String getDatabase() {
+        return database.getName();
+    }
+
+    @JsonProperty("database")
+    public void setDatabase(String database) {
+        this.database.setName(database);
+    }
+
+    public ColumnDesc[] getColumns() {
+        return columns;
+    }
+
+    public void setColumns(ColumnDesc[] columns) {
+        this.columns = columns;
+    }
+
+    public int getMaxColumnIndex() {
+        int max = -1;
+        for (ColumnDesc col : columns) {
+            int idx = col.getZeroBasedIndex();
+            max = Math.max(max, idx);
+        }
+        return max;
+    }
+
+    public int getColumnCount() {
+        return getMaxColumnIndex() + 1;
+    }
+
+    public void init() {
+        if (name != null)
+            name = name.toUpperCase();
+
+        if (getDatabase() != null)
+            setDatabase(getDatabase().toUpperCase());
+
+        if (columns != null) {
+            Arrays.sort(columns, new Comparator<ColumnDesc>() {
+                @Override
+                public int compare(ColumnDesc col1, ColumnDesc col2) {
+                    Integer id1 = Integer.parseInt(col1.getId());
+                    Integer id2 = Integer.parseInt(col2.getId());
+                    return id1.compareTo(id2);
+                }
+            });
+
+            for (ColumnDesc col : columns) {
+                col.init(this);
+            }
+        }
+    }
+
+    @Override
+    public String toString() {
+        return "TableDesc [database=" + getDatabase() + " name=" + name + "]";
+    }
+
+    /** create a mockup table for unit test */
+    public static TableDesc mockup(String tableName) {
+        TableDesc mockup = new TableDesc();
+        mockup.setName(tableName);
+        return mockup;
+    }
+}

http://git-wip-us.apache.org/repos/asf/incubator-kylin/blob/7e8896ac/core-metadata/src/main/java/org/apache/kylin/metadata/model/TblColRef.java
----------------------------------------------------------------------
diff --git a/core-metadata/src/main/java/org/apache/kylin/metadata/model/TblColRef.java b/core-metadata/src/main/java/org/apache/kylin/metadata/model/TblColRef.java
new file mode 100644
index 0000000..caac52a
--- /dev/null
+++ b/core-metadata/src/main/java/org/apache/kylin/metadata/model/TblColRef.java
@@ -0,0 +1,153 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you 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 org.apache.kylin.metadata.model;
+
+import org.apache.commons.lang.StringUtils;
+
+/**
+ */
+public class TblColRef {
+
+    private static final String INNER_TABLE_NAME = "_kylin_table";
+
+    // used by projection rewrite, see OLAPProjectRel
+    public enum InnerDataTypeEnum {
+
+        LITERAL("_literal_type"), DERIVED("_derived_type");
+
+        private final String dateType;
+
+        private InnerDataTypeEnum(String name) {
+            this.dateType = name;
+        }
+
+        public String getDataType() {
+            return dateType;
+        }
+
+        public static boolean contains(String name) {
+            return LITERAL.getDataType().equals(name) || DERIVED.getDataType().equals(name);
+        }
+    }
+
+    // used by projection rewrite, see OLAPProjectRel
+    public static TblColRef newInnerColumn(String columnName, InnerDataTypeEnum dataType) {
+        ColumnDesc column = new ColumnDesc();
+        column.setName(columnName);
+        TableDesc table = new TableDesc();
+        column.setTable(table);
+        TblColRef colRef = new TblColRef(column);
+        colRef.markInnerColumn(dataType);
+        return colRef;
+    }
+
+    // ============================================================================
+
+    private ColumnDesc column;
+
+    public TblColRef(ColumnDesc column) {
+        this.column = column;
+    }
+
+    public ColumnDesc getColumnDesc() {
+        return column;
+    }
+
+    public void setColumn(ColumnDesc column) {
+        this.column = column;
+    }
+
+    public String getName() {
+        return column.getName();
+    }
+
+    public String getTable() {
+        if (column.getTable() == null) {
+            return null;
+        }
+        return column.getTable().getIdentity();
+    }
+
+    public String getCanonicalName() {
+        return getTable() + "." + getName();
+    }
+
+    public String getDatatype() {
+        return column.getDatatype();
+    }
+
+    public DataType getType() {
+        return column.getType();
+    }
+
+    public void markInnerColumn(InnerDataTypeEnum dataType) {
+        this.column.setDatatype(dataType.getDataType());
+        this.column.getTable().setName(INNER_TABLE_NAME);
+        this.column.getTable().setDatabase("DEFAULT");
+    }
+
+    public boolean isInnerColumn() {
+        return InnerDataTypeEnum.contains(getDatatype());
+    }
+
+    public boolean isDerivedDataType() {
+        return InnerDataTypeEnum.DERIVED.getDataType().equals(getDatatype());
+    }
+
+    /**
+     *
+     * @param tableName full name : db.table
+     * @param columnName columnname
+     * @return
+     */
+    public boolean isSameAs(String tableName, String columnName) {
+        return column.isSameAs(tableName, columnName);
+    }
+
+    @Override
+    public int hashCode() {
+        final int prime = 31;
+        int result = 1;
+
+        result = prime * result + column.getTable().getIdentity().hashCode();
+        result = prime * result + column.getName().hashCode();
+        return result;
+    }
+
+    @Override
+    public boolean equals(Object obj) {
+        if (this == obj)
+            return true;
+        if (obj == null)
+            return false;
+        if (getClass() != obj.getClass())
+            return false;
+        TblColRef other = (TblColRef) obj;
+        if (!StringUtils.equals(column.getTable().getIdentity(), other.column.getTable().getIdentity()))
+            return false;
+        if (!StringUtils.equals(column.getName(), other.column.getName()))
+            return false;
+        return true;
+    }
+
+    @Override
+    public String toString() {
+        return (column.getTable() == null ? null : column.getTable().getIdentity()) + "." + column.getName();
+    }
+}

http://git-wip-us.apache.org/repos/asf/incubator-kylin/blob/7e8896ac/core-metadata/src/main/java/org/apache/kylin/metadata/project/ProjectInstance.java
----------------------------------------------------------------------
diff --git a/core-metadata/src/main/java/org/apache/kylin/metadata/project/ProjectInstance.java b/core-metadata/src/main/java/org/apache/kylin/metadata/project/ProjectInstance.java
new file mode 100644
index 0000000..d6cbfcc
--- /dev/null
+++ b/core-metadata/src/main/java/org/apache/kylin/metadata/project/ProjectInstance.java
@@ -0,0 +1,284 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you 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 org.apache.kylin.metadata.project;
+
+import com.fasterxml.jackson.annotation.JsonAutoDetect;
+import com.fasterxml.jackson.annotation.JsonAutoDetect.Visibility;
+import com.fasterxml.jackson.annotation.JsonProperty;
+import com.google.common.base.Predicate;
+import com.google.common.collect.ImmutableList;
+import com.google.common.collect.Iterables;
+import com.google.common.collect.Lists;
+import org.apache.commons.lang3.StringUtils;
+import org.apache.kylin.common.persistence.ResourceStore;
+import org.apache.kylin.common.persistence.RootPersistentEntity;
+import org.apache.kylin.metadata.realization.RealizationType;
+
+import javax.annotation.Nullable;
+import java.util.ArrayList;
+import java.util.List;
+import java.util.Set;
+import java.util.TreeSet;
+
+/**
+ * Project is a concept in Kylin similar to schema in DBMS
+ */
+@JsonAutoDetect(fieldVisibility = Visibility.NONE, getterVisibility = Visibility.NONE, isGetterVisibility = Visibility.NONE, setterVisibility = Visibility.NONE)
+public class ProjectInstance extends RootPersistentEntity {
+
+    public static final String DEFAULT_PROJECT_NAME = "DEFAULT";
+
+    @JsonProperty("name")
+    private String name;
+
+    @JsonProperty("tables")
+    private Set<String> tables = new TreeSet<String>();
+
+    @JsonProperty("owner")
+    private String owner;
+
+    @JsonProperty("status")
+    private ProjectStatusEnum status;
+
+    @JsonProperty("create_time_utc")
+    private long createTimeUTC;
+
+    @JsonProperty("last_update_time")
+    // FIXME why not RootPersistentEntity.lastModified??
+    private String lastUpdateTime;
+
+    @JsonProperty("description")
+    private String description;
+
+    @JsonProperty("realizations")
+    private List<RealizationEntry> realizationEntries;
+
+    @JsonProperty("models")
+    private List<String> models;
+
+    public String getResourcePath() {
+        return concatResourcePath(name);
+    }
+
+    public static String concatResourcePath(String projectName) {
+        return ResourceStore.PROJECT_RESOURCE_ROOT + "/" + projectName + ".json";
+    }
+
+    public static String getNormalizedProjectName(String project) {
+        if (project == null)
+            throw new IllegalStateException("Trying to normalized a project name which is null");
+
+        return project.toUpperCase();
+    }
+
+    public static ProjectInstance create(String name, String owner, String description, List<RealizationEntry> realizationEntries,List<String> models) {
+        ProjectInstance projectInstance = new ProjectInstance();
+
+        projectInstance.updateRandomUuid();
+        projectInstance.setName(name);
+        projectInstance.setOwner(owner);
+        projectInstance.setDescription(description);
+        projectInstance.setStatus(ProjectStatusEnum.ENABLED);
+        projectInstance.setCreateTimeUTC(System.currentTimeMillis());
+        if (realizationEntries != null)
+            projectInstance.setRealizationEntries(realizationEntries);
+        else
+            projectInstance.setRealizationEntries(Lists.<RealizationEntry> newArrayList());
+        if (models != null)
+            projectInstance.setModels(models);
+        else
+            projectInstance.setModels(new ArrayList<String>());
+        return projectInstance;
+    }
+
+    // ============================================================================
+
+    public ProjectInstance() {
+    }
+
+    public String getDescription() {
+        return description;
+    }
+
+    public void setDescription(String description) {
+        this.description = description;
+    }
+
+    public ProjectStatusEnum getStatus() {
+        return status;
+    }
+
+    public void setStatus(ProjectStatusEnum status) {
+        this.status = status;
+    }
+
+    public long getCreateTimeUTC() {
+        return createTimeUTC;
+    }
+
+    public void setCreateTimeUTC(long createTimeUTC) {
+        this.createTimeUTC = createTimeUTC;
+    }
+
+    public String getName() {
+        return this.name;
+    }
+
+    public void setName(String name) {
+        this.name = name;
+    }
+
+    public boolean containsRealization(final RealizationType type, final String realization) {
+        return Iterables.any(this.realizationEntries, new Predicate<RealizationEntry>() {
+            @Override
+            public boolean apply(RealizationEntry input) {
+                return input.getType() == type && input.getRealization().equalsIgnoreCase(realization);
+            }
+        });
+    }
+
+    public void removeRealization(final RealizationType type, final String realization) {
+        Iterables.removeIf(this.realizationEntries, new Predicate<RealizationEntry>() {
+            @Override
+            public boolean apply(RealizationEntry input) {
+                return input.getType() == type && input.getRealization().equalsIgnoreCase(realization);
+            }
+        });
+    }
+
+    public List<RealizationEntry> getRealizationEntries(final RealizationType type) {
+        if (type == null)
+            return getRealizationEntries();
+
+        return ImmutableList.copyOf(Iterables.filter(realizationEntries, new Predicate<RealizationEntry>() {
+            @Override
+            public boolean apply(@Nullable RealizationEntry input) {
+                return input.getType() == type;
+            }
+        }));
+    }
+
+    public int getRealizationCount(final RealizationType type) {
+
+        if (type == null)
+            return this.realizationEntries.size();
+
+        return Iterables.size(Iterables.filter(this.realizationEntries, new Predicate<RealizationEntry>() {
+            @Override
+            public boolean apply(RealizationEntry input) {
+                return input.getType() == type;
+            }
+        }));
+    }
+
+    public void addRealizationEntry(final RealizationType type, final String realizationName) {
+        RealizationEntry pdm = new RealizationEntry();
+        pdm.setType(type);
+        pdm.setRealization(realizationName);
+        this.realizationEntries.add(pdm);
+    }
+
+    public void setTables(Set<String> tables) {
+        this.tables = tables;
+    }
+
+    public boolean containsTable(String tableName) {
+        return tables.contains(tableName.toUpperCase());
+    }
+
+    public void removeTable(String tableName) {
+        tables.remove(tableName.toUpperCase());
+    }
+
+    public int getTablesCount() {
+        return this.getTables().size();
+    }
+
+    public void addTable(String tableName) {
+        this.getTables().add(tableName.toUpperCase());
+    }
+
+    public Set<String> getTables() {
+        return tables;
+    }
+
+    public String getOwner() {
+        return owner;
+    }
+
+    public void setOwner(String owner) {
+        this.owner = owner;
+    }
+
+    public void recordUpdateTime(long timeMillis) {
+        this.lastUpdateTime = formatTime(timeMillis);
+    }
+
+    public List<RealizationEntry> getRealizationEntries() {
+        return realizationEntries;
+    }
+
+    public void setRealizationEntries(List<RealizationEntry> entries) {
+        this.realizationEntries = entries;
+    }
+
+    public List<String> getModels() {
+        return models;
+    }
+
+    public boolean containsModel(String modelName) {
+        return models!=null&&models.contains(modelName);
+    }
+
+    public void setModels(List<String> models) { this.models = models; }
+
+    public void addModel(String modelName) {
+        if(this.getModels()==null){
+            this.setModels(new ArrayList<String>());
+        }
+        this.getModels().add(modelName);
+    }
+
+    public void removeModel(String modelName){
+        if(this.getModels()!=null) {
+            this.getModels().remove(modelName);
+        }
+    }
+
+    public void init() {
+        if (name == null)
+            name = ProjectInstance.DEFAULT_PROJECT_NAME;
+
+        if (realizationEntries == null) {
+            realizationEntries = new ArrayList<RealizationEntry>();
+        }
+
+        if (tables == null)
+            tables = new TreeSet<String>();
+
+        if (StringUtils.isBlank(this.name))
+            throw new IllegalStateException("Project name must not be blank");
+    }
+
+    @Override
+    public String toString() {
+        return "ProjectDesc [name=" + name + "]";
+    }
+
+}