You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@kylin.apache.org by xx...@apache.org on 2020/06/15 02:51:19 UTC

[kylin] 03/15: KYLIN-4421 Allow to update table & database name

This is an automated email from the ASF dual-hosted git repository.

xxyu pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/kylin.git

commit d494b8d05f75fd648d2dbb1dcb4b72f452de8b55
Author: Zhong, Yanghong <nj...@apache.org>
AuthorDate: Fri Apr 10 10:43:02 2020 +0800

    KYLIN-4421 Allow to update table & database name
---
 .../java/org/apache/kylin/cube/CubeInstance.java   |  65 ++-
 .../java/org/apache/kylin/cube/CubeSegment.java    |  41 ++
 .../apache/kylin/cube/model/AggregationGroup.java  |   2 +
 .../java/org/apache/kylin/cube/model/CubeDesc.java |  65 +++
 .../apache/kylin/cube/model/DictionaryDesc.java    |  17 +
 .../kylin/cube/model/HBaseColumnFamilyDesc.java    |  16 +
 .../org/apache/kylin/cube/model/SelectRule.java    |  40 ++
 .../apache/kylin/cube/model/SnapshotTableDesc.java |  33 +-
 .../apache/kylin/metadata/model/DataModelDesc.java |  46 ++
 .../apache/kylin/metadata/model/JoinTableDesc.java |   9 +
 .../kylin/metadata/model/ModelDimensionDesc.java   |  18 +
 .../apache/kylin/metadata/model/ParameterDesc.java |   7 +-
 .../org/apache/kylin/metadata/model/TableDesc.java |  14 +-
 .../service/update/TableSchemaUpdateMapping.java   |  71 +++
 .../rest/service/update/TableSchemaUpdater.java    | 191 +++++++
 .../service/update/TableSchemaUpdaterTest.java     | 154 +++++
 .../resources/update/TableSchemaUpdateMapping.json |  19 +
 .../test_kylin_cube_with_slr_left_join_ready.json  |  44 ++
 .../update/cube_desc/ci_left_join_cube.json        | 619 +++++++++++++++++++++
 .../update/model_desc/ci_inner_join_model.json     | 238 ++++++++
 .../test/resources/update/table/EDW.CAL_DT.json    | 413 ++++++++++++++
 .../test/resources/update/table/TEST.COUNTRY.json  |  26 +
 .../resources/update/table/TEST.KYLIN_FACT.json    |  73 +++
 .../resources/update/table/TEST.TEST_ACCOUNT.json  |  36 ++
 .../update/table/TEST.TEST_CATEGORY_GROUPINGS.json | 155 ++++++
 25 files changed, 2393 insertions(+), 19 deletions(-)

diff --git a/core-cube/src/main/java/org/apache/kylin/cube/CubeInstance.java b/core-cube/src/main/java/org/apache/kylin/cube/CubeInstance.java
index eb90d56..454816d 100644
--- a/core-cube/src/main/java/org/apache/kylin/cube/CubeInstance.java
+++ b/core-cube/src/main/java/org/apache/kylin/cube/CubeInstance.java
@@ -24,6 +24,7 @@ import static org.apache.kylin.cube.cuboid.CuboidModeEnum.RECOMMEND;
 
 import java.io.IOException;
 import java.nio.charset.StandardCharsets;
+import java.util.Arrays;
 import java.util.List;
 import java.util.Map;
 import java.util.Set;
@@ -273,6 +274,33 @@ public class CubeInstance extends RootPersistentEntity implements IRealization,
         return true;
     }
 
+    public boolean equalsRaw(Object o) {
+        if (this == o)
+            return true;
+        if (o == null || getClass() != o.getClass())
+            return false;
+
+        CubeInstance that = (CubeInstance) o;
+        if (!java.util.Objects.equals(name, that.name))
+            return false;
+        if (!java.util.Objects.equals(owner, that.owner))
+            return false;
+        if (!java.util.Objects.equals(descName, that.descName))
+            return false;
+        if (!java.util.Objects.equals(displayName, that.displayName))
+            return false;
+        if (!java.util.Objects.equals(status, that.status))
+            return false;
+
+        if (!java.util.Objects.equals(segments, that.segments))
+            return false;
+        if (!java.util.Arrays.equals(cuboidBytes, that.cuboidBytes))
+            return false;
+        if (!java.util.Arrays.equals(cuboidBytesRecommend, that.cuboidBytesRecommend))
+            return false;
+        return java.util.Objects.equals(snapshots, that.snapshots);
+    }
+
     // ============================================================================
 
     @Override
@@ -695,6 +723,10 @@ public class CubeInstance extends RootPersistentEntity implements IRealization,
         return snapshots;
     }
 
+    public void resetSnapshots() {
+        snapshots = Maps.newHashMap();
+    }
+
     public String getSnapshotResPath(String tableName) {
         return getSnapshots().get(tableName);
     }
@@ -703,18 +735,27 @@ public class CubeInstance extends RootPersistentEntity implements IRealization,
         getSnapshots().put(table, snapshotResPath);
     }
 
-    public static CubeInstance getCopyOf(CubeInstance cubeInstance) {
-        CubeInstance newCube = new CubeInstance();
-        newCube.setName(cubeInstance.getName());
-        newCube.setSegments(cubeInstance.getSegments());
-        newCube.setDescName(cubeInstance.getDescName());
-        newCube.setConfig((KylinConfigExt) cubeInstance.getConfig());
-        newCube.setStatus(cubeInstance.getStatus());
-        newCube.setOwner(cubeInstance.getOwner());
-        newCube.setCost(cubeInstance.getCost());
-        newCube.setCreateTimeUTC(System.currentTimeMillis());
-        newCube.updateRandomUuid();
-        return newCube;
+    public static CubeInstance getCopyOf(CubeInstance other) {
+        CubeInstance ret = new CubeInstance();
+        ret.setName(other.getName());
+        ret.setOwner(other.getOwner());
+        ret.setDescName(other.getDescName());
+        ret.setCost(other.getCost());
+        ret.setStatus(other.getStatus());
+        ret.setSegments(other.getSegments());
+        ret.setCreateTimeUTC(System.currentTimeMillis());
+        if (other.cuboidBytes != null) {
+            ret.cuboidBytes = Arrays.copyOf(other.cuboidBytes, other.cuboidBytes.length);
+        }
+        if (other.cuboidBytesRecommend != null) {
+            ret.cuboidBytesRecommend = Arrays.copyOf(other.cuboidBytesRecommend, other.cuboidBytesRecommend.length);
+        }
+        ret.cuboidLastOptimized = other.cuboidLastOptimized;
+        ret.getSnapshots().putAll(other.getSnapshots());
+
+        ret.setConfig((KylinConfigExt) other.getConfig());
+        ret.updateRandomUuid();
+        return ret;
     }
 
     public static CubeSegment findSegmentWithJobId(String jobID, CubeInstance cubeInstance) {
diff --git a/core-cube/src/main/java/org/apache/kylin/cube/CubeSegment.java b/core-cube/src/main/java/org/apache/kylin/cube/CubeSegment.java
index 52f2034..e423f57 100644
--- a/core-cube/src/main/java/org/apache/kylin/cube/CubeSegment.java
+++ b/core-cube/src/main/java/org/apache/kylin/cube/CubeSegment.java
@@ -321,6 +321,10 @@ public class CubeSegment implements IBuildable, ISegment, Serializable {
         return snapshots;
     }
 
+    public void resetSnapshots() {
+        snapshots = new ConcurrentHashMap<String, String>();
+    }
+
     public String getSnapshotResPath(String table) {
         return getSnapshots().get(table);
     }
@@ -660,4 +664,41 @@ public class CubeSegment implements IBuildable, ISegment, Serializable {
     public void setStreamSourceCheckpoint(String streamSourceCheckpoint) {
         this.streamSourceCheckpoint = streamSourceCheckpoint;
     }
+
+    public static CubeSegment getCopyOf(CubeSegment other) {
+        CubeSegment copy = new CubeSegment();
+        copy.cubeInstance = other.cubeInstance;
+        copy.uuid = other.uuid;
+        copy.name = other.name;
+        copy.storageLocationIdentifier = other.storageLocationIdentifier;
+        copy.dateRangeStart = other.dateRangeStart;
+        copy.dateRangeEnd = other.dateRangeEnd;
+        copy.sourceOffsetStart = other.sourceOffsetStart;
+        copy.sourceOffsetEnd = other.sourceOffsetEnd;
+        copy.status = other.status;
+        copy.sizeKB = other.sizeKB;
+        copy.isMerged = other.isMerged;
+        copy.estimateRatio = other.estimateRatio == null ? null : Lists.newArrayList(other.estimateRatio);
+        copy.inputRecords = other.inputRecords;
+        copy.inputRecordsSize = other.inputRecordsSize;
+        copy.lastBuildTime = other.lastBuildTime;
+        copy.lastBuildJobID = other.lastBuildJobID;
+        copy.createTimeUTC = other.createTimeUTC;
+        copy.cuboidShardNums.putAll(other.cuboidShardNums);
+        copy.totalShards = other.totalShards;
+        copy.blackoutCuboids.addAll(other.blackoutCuboids);
+        copy.getDictionaries().putAll(other.getDictionaries());
+        copy.getSnapshots().putAll(other.getSnapshots());
+        copy.rowkeyStats.addAll(other.rowkeyStats);
+        copy.sourcePartitionOffsetStart.putAll(other.sourcePartitionOffsetStart);
+        copy.sourcePartitionOffsetEnd.putAll(other.sourcePartitionOffsetEnd);
+        if (other.streamSourceCheckpoint != null) {
+            copy.streamSourceCheckpoint = other.streamSourceCheckpoint;
+        }
+        copy.additionalInfo.putAll(other.additionalInfo);
+        copy.dimensionRangeInfoMap = other.dimensionRangeInfoMap == null ? null
+                : Maps.newHashMap(other.dimensionRangeInfoMap);
+        copy.binarySignature = other.binarySignature;
+        return copy;
+    }
 }
diff --git a/core-cube/src/main/java/org/apache/kylin/cube/model/AggregationGroup.java b/core-cube/src/main/java/org/apache/kylin/cube/model/AggregationGroup.java
index 7de211e..c03cca1 100644
--- a/core-cube/src/main/java/org/apache/kylin/cube/model/AggregationGroup.java
+++ b/core-cube/src/main/java/org/apache/kylin/cube/model/AggregationGroup.java
@@ -540,4 +540,6 @@ public class AggregationGroup implements Serializable {
     public int getDimCap() {
         return this.selectRule.dimCap == null ? 0 : this.selectRule.dimCap;
     }
+
+
 }
diff --git a/core-cube/src/main/java/org/apache/kylin/cube/model/CubeDesc.java b/core-cube/src/main/java/org/apache/kylin/cube/model/CubeDesc.java
index 364ad6d..13311c0 100644
--- a/core-cube/src/main/java/org/apache/kylin/cube/model/CubeDesc.java
+++ b/core-cube/src/main/java/org/apache/kylin/cube/model/CubeDesc.java
@@ -42,6 +42,7 @@ import java.util.Map.Entry;
 import java.util.Objects;
 import java.util.Set;
 import java.util.TreeSet;
+import java.util.stream.IntStream;
 
 import org.apache.commons.codec.binary.Base64;
 import org.apache.commons.collections.CollectionUtils;
@@ -484,6 +485,70 @@ public class CubeDesc extends RootPersistentEntity implements IEngineAware {
         return mandatoryCuboids;
     }
 
+    public boolean equalsRaw(Object o) {
+        if (this == o)
+            return true;
+        if (o == null || getClass() != o.getClass())
+            return false;
+
+        CubeDesc that = (CubeDesc) o;
+
+        if (!Objects.equals(name, that.name))
+            return false;
+        if (!Objects.equals(modelName, that.modelName))
+            return false;
+        if (!Objects.equals(description, that.description))
+            return false;
+        if (!Objects.equals(dimensions, that.dimensions))
+            return false;
+        if (!Objects.equals(measures, that.measures))
+            return false;
+        if (!Objects.equals(dictionaries, that.dictionaries))
+            return false;
+        if (!Arrays.equals(rowkey.getRowKeyColumns(), that.rowkey.getRowKeyColumns()))
+            return false;
+        if (!Objects.equals(nullStrings, that.nullStrings))
+            return false;
+        if (!Arrays.equals(hbaseMapping.getColumnFamily(), that.hbaseMapping.getColumnFamily()))
+            return false;
+        if (aggregationGroups != that.aggregationGroups) {
+            if (aggregationGroups == null || that.aggregationGroups == null) {
+                return false;
+            } else if (!IntStream.range(0, aggregationGroups.size())
+                    .allMatch(i -> Arrays.equals(aggregationGroups.get(i).getIncludes(),
+                            that.aggregationGroups.get(i).getIncludes())
+                            && Objects.equals(aggregationGroups.get(i).getSelectRule(),
+                                    that.aggregationGroups.get(i).getSelectRule()))) {
+                return false;
+            }
+        }
+        if (!Objects.equals(notifyList, that.notifyList))
+            return false;
+        if (!Objects.equals(statusNeedNotify, that.statusNeedNotify))
+            return false;
+        if (!Arrays.equals(autoMergeTimeRanges, that.autoMergeTimeRanges))
+            return false;
+        if (!Objects.equals(retentionRange, that.retentionRange))
+            return false;
+        if (!Objects.equals(engineType, that.engineType))
+            return false;
+        if (!Objects.equals(storageType, that.storageType))
+            return false;
+        if (!Objects.equals(overrideKylinProps, that.overrideKylinProps))
+            return false;
+        if (!Objects.equals(snapshotTableDescList, that.snapshotTableDescList))
+            return false;
+        if (!Objects.equals(partitionDateStart, that.partitionDateStart))
+            return false;
+        if (!Objects.equals(partitionDateEnd, that.partitionDateEnd))
+            return false;
+        if (!Objects.equals(parentForward, that.parentForward))
+            return false;
+        if (!Objects.equals(mandatoryDimensionSetList, that.mandatoryDimensionSetList))
+            return false;
+        return Objects.equals(cuboidBlackSet, that.cuboidBlackSet);
+    }
+
     @Override
     public boolean equals(Object o) {
         if (this == o)
diff --git a/core-cube/src/main/java/org/apache/kylin/cube/model/DictionaryDesc.java b/core-cube/src/main/java/org/apache/kylin/cube/model/DictionaryDesc.java
index 2d1ba99..248984e 100644
--- a/core-cube/src/main/java/org/apache/kylin/cube/model/DictionaryDesc.java
+++ b/core-cube/src/main/java/org/apache/kylin/cube/model/DictionaryDesc.java
@@ -118,4 +118,21 @@ public class DictionaryDesc implements java.io.Serializable {
         desc.builderClass = builderClass;
         return desc;
     }
+
+    @Override
+    public boolean equals(Object o) {
+        if (this == o) return true;
+        if (o == null || getClass() != o.getClass()) return false;
+        DictionaryDesc that = (DictionaryDesc) o;
+        return Objects.equals(column, that.column) &&
+                Objects.equals(reuseColumn, that.reuseColumn) &&
+                Objects.equals(builderClass, that.builderClass) &&
+                Objects.equals(cube, that.cube) &&
+                Objects.equals(model, that.model);
+    }
+
+    @Override
+    public int hashCode() {
+        return Objects.hash(column, reuseColumn, builderClass, cube, model);
+    }
 }
diff --git a/core-cube/src/main/java/org/apache/kylin/cube/model/HBaseColumnFamilyDesc.java b/core-cube/src/main/java/org/apache/kylin/cube/model/HBaseColumnFamilyDesc.java
index 85c2c17..7550751 100644
--- a/core-cube/src/main/java/org/apache/kylin/cube/model/HBaseColumnFamilyDesc.java
+++ b/core-cube/src/main/java/org/apache/kylin/cube/model/HBaseColumnFamilyDesc.java
@@ -24,6 +24,7 @@ import com.fasterxml.jackson.annotation.JsonProperty;
 import org.apache.kylin.metadata.model.MeasureDesc;
 
 import java.util.Arrays;
+import java.util.Objects;
 
 /**
  */
@@ -67,4 +68,19 @@ public class HBaseColumnFamilyDesc implements java.io.Serializable {
         return false;
     }
 
+    @Override
+    public boolean equals(Object o) {
+        if (this == o) return true;
+        if (o == null || getClass() != o.getClass()) return false;
+        HBaseColumnFamilyDesc that = (HBaseColumnFamilyDesc) o;
+        return Objects.equals(name, that.name) &&
+                Arrays.equals(columns, that.columns);
+    }
+
+    @Override
+    public int hashCode() {
+        int result = Objects.hash(name);
+        result = 31 * result + Arrays.hashCode(columns);
+        return result;
+    }
 }
diff --git a/core-cube/src/main/java/org/apache/kylin/cube/model/SelectRule.java b/core-cube/src/main/java/org/apache/kylin/cube/model/SelectRule.java
index d78da9f..b3527ae 100644
--- a/core-cube/src/main/java/org/apache/kylin/cube/model/SelectRule.java
+++ b/core-cube/src/main/java/org/apache/kylin/cube/model/SelectRule.java
@@ -19,6 +19,9 @@
 package org.apache.kylin.cube.model;
 
 import java.io.Serializable;
+import java.util.Arrays;
+import java.util.Objects;
+import java.util.stream.IntStream;
 
 import com.fasterxml.jackson.annotation.JsonInclude;
 import com.fasterxml.jackson.annotation.JsonProperty;
@@ -35,4 +38,41 @@ public class SelectRule implements Serializable {
     @JsonProperty("dim_cap")
     @JsonInclude(JsonInclude.Include.NON_NULL)
     public Integer dimCap;
+
+    @Override
+    public boolean equals(Object o) {
+        if (this == o)
+            return true;
+        if (o == null || getClass() != o.getClass())
+            return false;
+
+        SelectRule that = (SelectRule) o;
+        if (hierarchyDims != that.hierarchyDims) {
+            if (hierarchyDims == null || that.hierarchyDims == null) {
+                return false;
+            } else if (!IntStream.range(0, hierarchyDims.length)
+                    .allMatch(i -> Arrays.equals(hierarchyDims[i], that.hierarchyDims[i]))) {
+                return false;
+            }
+        }
+
+        if (jointDims != that.jointDims) {
+            if (jointDims == null || that.jointDims == null) {
+                return false;
+            } else if (!IntStream.range(0, jointDims.length)
+                    .allMatch(i -> Arrays.equals(jointDims[i], that.jointDims[i]))) {
+                return false;
+            }
+        }
+        return Arrays.equals(mandatoryDims, that.mandatoryDims) && Objects.equals(dimCap, that.dimCap);
+    }
+
+    @Override
+    public int hashCode() {
+        int result = Objects.hash(dimCap);
+        result = 31 * result + Arrays.hashCode(hierarchyDims);
+        result = 31 * result + Arrays.hashCode(mandatoryDims);
+        result = 31 * result + Arrays.hashCode(jointDims);
+        return result;
+    }
 }
diff --git a/core-cube/src/main/java/org/apache/kylin/cube/model/SnapshotTableDesc.java b/core-cube/src/main/java/org/apache/kylin/cube/model/SnapshotTableDesc.java
index e61240b..30f533b 100644
--- a/core-cube/src/main/java/org/apache/kylin/cube/model/SnapshotTableDesc.java
+++ b/core-cube/src/main/java/org/apache/kylin/cube/model/SnapshotTableDesc.java
@@ -18,13 +18,17 @@
 
 package org.apache.kylin.cube.model;
 
+import org.apache.kylin.dict.lookup.SnapshotTable;
+
 import com.fasterxml.jackson.annotation.JsonAutoDetect;
 import com.fasterxml.jackson.annotation.JsonAutoDetect.Visibility;
 import com.fasterxml.jackson.annotation.JsonProperty;
-import org.apache.kylin.dict.lookup.SnapshotTable;
+
+import java.util.Objects;
 
 @JsonAutoDetect(fieldVisibility = Visibility.NONE, getterVisibility = Visibility.NONE, isGetterVisibility = Visibility.NONE, setterVisibility = Visibility.NONE)
-public class SnapshotTableDesc implements java.io.Serializable{
+public class
+SnapshotTableDesc implements java.io.Serializable{
     @JsonProperty("table_name")
     private String tableName;
 
@@ -72,4 +76,29 @@ public class SnapshotTableDesc implements java.io.Serializable{
     public void setEnableLocalCache(boolean enableLocalCache) {
         this.enableLocalCache = enableLocalCache;
     }
+
+    public static SnapshotTableDesc getCopyOf(SnapshotTableDesc other) {
+        SnapshotTableDesc copy = new SnapshotTableDesc();
+        copy.tableName = other.tableName;
+        copy.storageType = other.storageType;
+        copy.enableLocalCache = other.enableLocalCache;
+        copy.global = other.global;
+        return copy;
+    }
+
+    @Override
+    public boolean equals(Object o) {
+        if (this == o) return true;
+        if (o == null || getClass() != o.getClass()) return false;
+        SnapshotTableDesc that = (SnapshotTableDesc) o;
+        return enableLocalCache == that.enableLocalCache &&
+                global == that.global &&
+                Objects.equals(tableName, that.tableName) &&
+                Objects.equals(storageType, that.storageType);
+    }
+
+    @Override
+    public int hashCode() {
+        return Objects.hash(tableName, storageType, enableLocalCache, global);
+    }
 }
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
index e117176..d126d3d 100644
--- 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
@@ -720,6 +720,52 @@ public class DataModelDesc extends RootPersistentEntity {
         return this.errors;
     }
 
+    private Map<String, JoinTableDesc> getJoinTableMap(JoinTableDesc[] joinTables) {
+        if (joinTables == null) {
+            return Maps.newHashMap();
+        }
+        Map<String, JoinTableDesc> ret = Maps.newHashMapWithExpectedSize(joinTables.length);
+        for (JoinTableDesc joinTable : joinTables) {
+            ret.put(joinTable.getAlias(), joinTable);
+        }
+        return ret;
+    }
+
+    public boolean equalsRaw(Object o) {
+        if (this == o)
+            return true;
+        if (o == null || getClass() != o.getClass())
+            return false;
+
+        DataModelDesc that = (DataModelDesc) o;
+
+        if (isDraft != that.isDraft)
+            return false;
+        if (name != null ? !name.equals(that.name) : that.name != null)
+            return false;
+        if (owner != null ? !owner.equals(that.owner) : that.owner != null)
+            return false;
+        if (description != null ? !description.equals(that.description) : that.description != null)
+            return false;
+        if (rootFactTable != null ? !rootFactTable.equals(that.rootFactTable) : that.rootFactTable != null)
+            return false;
+        if (rootFactTableAlias != null ? !rootFactTableAlias.equals(that.rootFactTableAlias)
+                : that.rootFactTableAlias != null)
+            return false;
+        if (!getJoinTableMap(joinTables).equals(getJoinTableMap(that.joinTables)))
+            return false;
+        if (dimensions != null ? !dimensions.equals(that.dimensions) : that.dimensions != null)
+            return false;
+        // Probably incorrect - comparing Object[] arrays with Arrays.equals
+        if (!Arrays.equals(metrics, that.metrics))
+            return false;
+        if (filterCondition != null ? !filterCondition.equals(that.filterCondition) : that.filterCondition != null)
+            return false;
+        if (partitionDesc != null ? !partitionDesc.equalsRaw(that.partitionDesc) : that.partitionDesc != null)
+            return false;
+        return capacity == that.capacity;
+    }
+
     @Override
     public boolean equals(Object o) {
         if (this == o)
diff --git a/core-metadata/src/main/java/org/apache/kylin/metadata/model/JoinTableDesc.java b/core-metadata/src/main/java/org/apache/kylin/metadata/model/JoinTableDesc.java
index dc4710e..f0d9111 100644
--- a/core-metadata/src/main/java/org/apache/kylin/metadata/model/JoinTableDesc.java
+++ b/core-metadata/src/main/java/org/apache/kylin/metadata/model/JoinTableDesc.java
@@ -87,6 +87,15 @@ public class JoinTableDesc implements Serializable {
         this.tableRef = ref;
     }
 
+    public static JoinTableDesc getCopyOf(JoinTableDesc other) {
+        JoinTableDesc copy = new JoinTableDesc();
+        copy.table = other.table;
+        copy.kind = other.kind;
+        copy.alias = other.alias;
+        copy.join = other.join;
+        return copy;
+    }
+
     @Override
     public boolean equals(Object o) {
         if (this == o)
diff --git a/core-metadata/src/main/java/org/apache/kylin/metadata/model/ModelDimensionDesc.java b/core-metadata/src/main/java/org/apache/kylin/metadata/model/ModelDimensionDesc.java
index caadbd6..cf729a4 100644
--- a/core-metadata/src/main/java/org/apache/kylin/metadata/model/ModelDimensionDesc.java
+++ b/core-metadata/src/main/java/org/apache/kylin/metadata/model/ModelDimensionDesc.java
@@ -19,9 +19,12 @@
 package org.apache.kylin.metadata.model;
 
 import java.io.Serializable;
+import java.util.Arrays;
 import java.util.List;
 
 import java.util.Locale;
+import java.util.Objects;
+
 import org.apache.kylin.common.util.StringUtil;
 
 import com.fasterxml.jackson.annotation.JsonAutoDetect;
@@ -94,4 +97,19 @@ public class ModelDimensionDesc implements Serializable {
         return count;
     }
 
+    @Override
+    public boolean equals(Object o) {
+        if (this == o) return true;
+        if (o == null || getClass() != o.getClass()) return false;
+        ModelDimensionDesc that = (ModelDimensionDesc) o;
+        return Objects.equals(table, that.table) &&
+                Arrays.equals(columns, that.columns);
+    }
+
+    @Override
+    public int hashCode() {
+        int result = Objects.hash(table);
+        result = 31 * result + Arrays.hashCode(columns);
+        return result;
+    }
 }
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
index 0633d1e..7748719 100644
--- 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
@@ -24,6 +24,7 @@ import java.util.ArrayList;
 import java.util.Arrays;
 import java.util.List;
 import java.util.Locale;
+import java.util.Objects;
 import java.util.Set;
 
 import com.fasterxml.jackson.annotation.JsonAutoDetect;
@@ -266,19 +267,19 @@ public class ParameterDesc implements Serializable {
 
             PlainParameter that = (PlainParameter) o;
 
-            if (type != null ? !type.equals(that.type) : that.type != null)
+            if (!Objects.equals(type, that.type))
                 return false;
 
             if (this.isColumnType()) {
                 if (!that.isColumnType())
                     return false;
-                if (!this.colRef.equals(that.colRef)) {
+                if (!Objects.equals(colRef, that.colRef)) {
                     return false;
                 }
             } else {
                 if (that.isColumnType())
                     return false;
-                if (!this.value.equals(that.value))
+                if (!Objects.equals(value, that.value))
                     return false;
             }
 
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
index 588cf1e..d99ff54 100644
--- 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
@@ -237,8 +237,7 @@ public class TableDesc extends RootPersistentEntity implements ISourceAware {
 
     public String getIdentity() {
         if (identity == null) {
-            identity = String.format(Locale.ROOT, "%s.%s", this.getDatabase().toUpperCase(Locale.ROOT), this.getName())
-                    .toUpperCase(Locale.ROOT);
+            setIdentity();
         }
         return identity;
     }
@@ -279,6 +278,9 @@ public class TableDesc extends RootPersistentEntity implements ISourceAware {
         } else {
             this.name = null;
         }
+        if (identity != null) {
+            setIdentity();
+        }
     }
 
     @JsonProperty("database")
@@ -289,6 +291,14 @@ public class TableDesc extends RootPersistentEntity implements ISourceAware {
     @JsonProperty("database")
     public void setDatabase(String database) {
         this.database.setName(database);
+        if (identity != null) {
+            setIdentity();
+        }
+    }
+
+    private void setIdentity() {
+        identity = String.format(Locale.ROOT, "%s.%s", this.getDatabase().toUpperCase(Locale.ROOT), this.getName())
+                .toUpperCase(Locale.ROOT);
     }
 
     public ColumnDesc[] getColumns() {
diff --git a/server-base/src/main/java/org/apache/kylin/rest/service/update/TableSchemaUpdateMapping.java b/server-base/src/main/java/org/apache/kylin/rest/service/update/TableSchemaUpdateMapping.java
new file mode 100644
index 0000000..62b4a35
--- /dev/null
+++ b/server-base/src/main/java/org/apache/kylin/rest/service/update/TableSchemaUpdateMapping.java
@@ -0,0 +1,71 @@
+/*
+ * 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.rest.service.update;
+
+import java.util.Locale;
+
+import com.google.common.base.Preconditions;
+import com.google.common.base.Strings;
+
+public class TableSchemaUpdateMapping {
+
+    private String database;
+
+    private String tableName;
+
+    public boolean isDatabaseChanged() {
+        return !Strings.isNullOrEmpty(database);
+    }
+
+    public String getDatabase(String dbName) {
+        String ret = isDatabaseChanged() ? database : dbName;
+        return ret.toUpperCase(Locale.ROOT);
+    }
+
+    public void setDatabase(String database) {
+        this.database = database;
+    }
+
+    public boolean isTableNameChanged() {
+        return !Strings.isNullOrEmpty(tableName);
+    }
+
+    public String getTableName(String tblName) {
+        String ret = isTableNameChanged() ? tableName : tblName;
+        return ret.toUpperCase(Locale.ROOT);
+    }
+
+    public void setTableName(String tableName) {
+        this.tableName = tableName;
+    }
+
+    public boolean isTableIdentityChanged() {
+        return isDatabaseChanged() || isTableNameChanged();
+    }
+
+    public String getTableIdentity(String tableIdentity) {
+        String[] tableNameEs = tableIdentity.split("\\.");
+        Preconditions.checkArgument(tableNameEs.length == 2);
+        return getTableIdentity(tableNameEs[0], tableNameEs[1]);
+    }
+
+    public String getTableIdentity(String database, String tableName) {
+        return getDatabase(database) + "." + getTableName(tableName);
+    }
+}
\ No newline at end of file
diff --git a/server-base/src/main/java/org/apache/kylin/rest/service/update/TableSchemaUpdater.java b/server-base/src/main/java/org/apache/kylin/rest/service/update/TableSchemaUpdater.java
new file mode 100644
index 0000000..99c88a0
--- /dev/null
+++ b/server-base/src/main/java/org/apache/kylin/rest/service/update/TableSchemaUpdater.java
@@ -0,0 +1,191 @@
+/*
+ * 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.rest.service.update;
+
+import java.util.List;
+import java.util.Locale;
+import java.util.Map;
+
+import org.apache.kylin.cube.CubeInstance;
+import org.apache.kylin.cube.CubeSegment;
+import org.apache.kylin.cube.model.CubeDesc;
+import org.apache.kylin.cube.model.SnapshotTableDesc;
+import org.apache.kylin.metadata.model.DataModelDesc;
+import org.apache.kylin.metadata.model.JoinTableDesc;
+import org.apache.kylin.metadata.model.PartitionDesc;
+import org.apache.kylin.metadata.model.Segments;
+import org.apache.kylin.metadata.model.TableDesc;
+
+import com.google.common.base.Strings;
+import com.google.common.collect.Lists;
+import com.google.common.collect.Maps;
+
+public class TableSchemaUpdater {
+
+    public static TableDesc dealWithMappingForTable(TableDesc other, Map<String, TableSchemaUpdateMapping> mappings) {
+        TableSchemaUpdateMapping mapping = getTableSchemaUpdateMapping(mappings, other.getIdentity());
+        if (mapping == null) {
+            return other;
+        }
+
+        TableDesc copy = new TableDesc(other);
+
+        copy.setDatabase(mapping.getDatabase(other.getDatabase()));
+
+        copy.setName(mapping.getTableName(other.getName()));
+
+        // It will always be a new one
+        copy.setLastModified(0L);
+
+        return copy;
+    }
+
+    // the input data model should be initialized, then table names & col names will be normalized
+    public static DataModelDesc dealWithMappingForModel(DataModelDesc other,
+            Map<String, TableSchemaUpdateMapping> mappings) {
+        // For filter condition, not support
+        if (!Strings.isNullOrEmpty(other.getFilterCondition())) {
+            throw new UnsupportedOperationException("Cannot deal with filter condition " + other.getFilterCondition());
+        }
+
+        DataModelDesc copy = DataModelDesc.getCopyOf(other);
+        copy.setLastModified(other.getLastModified());
+
+        // mapping for root fact table identity
+        TableSchemaUpdateMapping rootMapping = getTableSchemaUpdateMapping(mappings, other.getRootFactTableName());
+        if (rootMapping != null) {
+            TableDesc rootFactTable = other.getRootFactTable().getTableDesc();
+            copy.setRootFactTableName(
+                    rootMapping.getTableIdentity(rootFactTable.getDatabase(), rootFactTable.getName()));
+        }
+
+        // mapping for joins
+        JoinTableDesc[] joinTables = other.getJoinTables();
+        JoinTableDesc[] joinTablesCopy = new JoinTableDesc[joinTables.length];
+        for (int i = 0; i < joinTables.length; i++) {
+            JoinTableDesc joinTable = joinTables[i];
+            joinTablesCopy[i] = JoinTableDesc.getCopyOf(joinTable);
+            String tableIdentity = joinTable.getTable();
+            TableSchemaUpdateMapping mapping = getTableSchemaUpdateMapping(mappings, tableIdentity);
+            if (mapping != null && mapping.isTableIdentityChanged()) {
+                joinTablesCopy[i].setTable(mapping.getTableIdentity(tableIdentity));
+            }
+        }
+        copy.setJoinTables(joinTablesCopy);
+
+        // mapping for partition columns
+        PartitionDesc partDesc = other.getPartitionDesc();
+        PartitionDesc partCopy = PartitionDesc.getCopyOf(partDesc);
+        if (partDesc.getPartitionDateColumnRef() != null) {
+            partCopy.setPartitionDateColumn(
+                    replacePartitionCol(partDesc.getPartitionDateColumnRef().getCanonicalName(), mappings));
+        }
+        if (partDesc.getPartitionTimeColumnRef() != null) {
+            partCopy.setPartitionTimeColumn(
+                    replacePartitionCol(partDesc.getPartitionTimeColumnRef().getCanonicalName(), mappings));
+        }
+        copy.setPartitionDesc(partCopy);
+
+        return copy;
+    }
+
+    public static CubeDesc dealWithMappingForCubeDesc(CubeDesc other, Map<String, TableSchemaUpdateMapping> mappings) {
+        CubeDesc copy = CubeDesc.getCopyOf(other);
+        copy.setLastModified(other.getLastModified());
+
+        // mapping for cube-level snapshot tables
+        if (other.getSnapshotTableDescList() != null && !other.getSnapshotTableDescList().isEmpty()) {
+            List<SnapshotTableDesc> snapshotTableDescListCopy = Lists
+                    .newArrayListWithExpectedSize(other.getSnapshotTableDescList().size());
+            for (SnapshotTableDesc snapshotDesc : other.getSnapshotTableDescList()) {
+                TableSchemaUpdateMapping mapping = getTableSchemaUpdateMapping(mappings, snapshotDesc.getTableName());
+                if (mapping != null && mapping.isTableIdentityChanged()) {
+                    snapshotDesc = SnapshotTableDesc.getCopyOf(snapshotDesc);
+                    snapshotDesc.setTableName(mapping.getTableIdentity(snapshotDesc.getTableName()));
+                }
+                snapshotTableDescListCopy.add(snapshotDesc);
+            }
+            copy.setSnapshotTableDescList(snapshotTableDescListCopy);
+        }
+
+        return copy;
+    }
+
+    public static CubeInstance dealWithMappingForCube(CubeInstance other,
+            Map<String, TableSchemaUpdateMapping> mappings) {
+        CubeInstance copy = CubeInstance.getCopyOf(other);
+        copy.setLastModified(other.getLastModified());
+
+        // mapping for cube-level snapshot tables
+        if (other.getSnapshots() != null && !other.getSnapshots().isEmpty()) {
+            Map<String, String> snapshotsCopy = replaceTableIdentityForTableSnapshots(other.getSnapshots(), mappings);
+            copy.resetSnapshots();
+            copy.getSnapshots().putAll(snapshotsCopy);
+        }
+
+        // mapping for segment-level snapshot tables
+        if (other.getSegments() != null && !other.getSegments().isEmpty()) {
+            Segments<CubeSegment> segmentsCopy = new Segments<>();
+            for (CubeSegment segment : other.getSegments()) {
+                CubeSegment segmentCopy = CubeSegment.getCopyOf(segment);
+                segmentCopy.setCubeInstance(copy);
+                Map<String, String> snapshotsCopy = replaceTableIdentityForTableSnapshots(segment.getSnapshots(),
+                        mappings);
+                segmentCopy.resetSnapshots();
+                segmentCopy.getSnapshots().putAll(snapshotsCopy);
+                segmentsCopy.add(segmentCopy);
+            }
+            copy.setSegments(segmentsCopy);
+        }
+
+        return copy;
+    }
+
+    private static Map<String, String> replaceTableIdentityForTableSnapshots(Map<String, String> snapshots,
+            Map<String, TableSchemaUpdateMapping> mappings) {
+        Map<String, String> snapshotsCopy = Maps.newHashMapWithExpectedSize(snapshots.size());
+        for (String tableIdentity : snapshots.keySet()) {
+            String resPath = snapshots.get(tableIdentity);
+            TableSchemaUpdateMapping mapping = getTableSchemaUpdateMapping(mappings, tableIdentity);
+            if (mapping != null && mapping.isTableIdentityChanged()) {
+                tableIdentity = mapping.getTableIdentity(tableIdentity);
+            }
+            snapshotsCopy.put(tableIdentity, resPath);
+        }
+        return snapshotsCopy;
+    }
+
+    private static String replacePartitionCol(String partCol, Map<String, TableSchemaUpdateMapping> mappings) {
+        int cut = partCol.lastIndexOf('.');
+        if (cut < 0) {
+            return partCol;
+        }
+        String partTableIdentity = partCol.substring(0, cut);
+        TableSchemaUpdateMapping mapping = getTableSchemaUpdateMapping(mappings, partTableIdentity);
+        if (mapping != null) {
+            return mapping.getTableIdentity(partTableIdentity) + "." + partCol.substring(cut + 1);
+        }
+        return partCol;
+    }
+
+    public static TableSchemaUpdateMapping getTableSchemaUpdateMapping(Map<String, TableSchemaUpdateMapping> mappings,
+            String key) {
+        return mappings.get(key.toUpperCase(Locale.ROOT));
+    }
+}
diff --git a/server-base/src/test/java/org/apache/kylin/rest/service/update/TableSchemaUpdaterTest.java b/server-base/src/test/java/org/apache/kylin/rest/service/update/TableSchemaUpdaterTest.java
new file mode 100644
index 0000000..7b8eecb
--- /dev/null
+++ b/server-base/src/test/java/org/apache/kylin/rest/service/update/TableSchemaUpdaterTest.java
@@ -0,0 +1,154 @@
+/*
+ * 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.rest.service.update;
+
+import java.io.ByteArrayInputStream;
+import java.io.ByteArrayOutputStream;
+import java.io.DataInputStream;
+import java.io.DataOutputStream;
+import java.io.File;
+import java.io.FileInputStream;
+import java.io.FileNotFoundException;
+import java.io.IOException;
+import java.nio.charset.StandardCharsets;
+import java.nio.file.Files;
+import java.util.Map;
+import java.util.Set;
+
+import org.apache.kylin.common.persistence.ResourceStore;
+import org.apache.kylin.common.persistence.RootPersistentEntity;
+import org.apache.kylin.common.persistence.Serializer;
+import org.apache.kylin.common.util.JsonUtil;
+import org.apache.kylin.common.util.LocalFileMetadataTestCase;
+import org.apache.kylin.cube.CubeDescManager;
+import org.apache.kylin.cube.CubeInstance;
+import org.apache.kylin.cube.CubeManager;
+import org.apache.kylin.cube.model.CubeDesc;
+import org.apache.kylin.metadata.TableMetadataManager;
+import org.apache.kylin.metadata.model.DataModelDesc;
+import org.apache.kylin.metadata.model.DataModelManager;
+import org.apache.kylin.metadata.model.TableDesc;
+import org.junit.Assert;
+import org.junit.Before;
+import org.junit.Test;
+
+import com.fasterxml.jackson.core.type.TypeReference;
+import com.google.common.collect.Sets;
+
+public class TableSchemaUpdaterTest extends LocalFileMetadataTestCase {
+
+    private final String mappingRootPath = "src/test/resources/update";
+    private final String mappingFileName = "TableSchemaUpdateMapping.json";
+    private Map<String, TableSchemaUpdateMapping> mappings;
+
+    @Before
+    public void setUp() throws IOException {
+        this.createTestMetadata();
+
+        File mappingFile = new File(mappingRootPath + "/" + mappingFileName);
+        String content = new String(Files.readAllBytes(mappingFile.toPath()), StandardCharsets.UTF_8);
+        mappings = JsonUtil.readValue(content, new TypeReference<Map<String, TableSchemaUpdateMapping>>() {
+        });
+    }
+
+    @Test
+    public void testDealWithMappingForTable() throws IOException {
+        TableMetadataManager tableMetaManager = TableMetadataManager.getInstance(getTestConfig());
+        ResourceStore store = tableMetaManager.getStore();
+
+        Set<TableDesc> tables = Sets.newHashSet();
+        for (String tableIdentity : mappings.keySet()) {
+            tables.add(store.getResource(TableDesc.concatResourcePath(tableIdentity, null),
+                    TableMetadataManager.TABLE_SERIALIZER));
+        }
+
+        for (TableDesc tableDesc : tables) {
+            TableDesc updated = TableSchemaUpdater.dealWithMappingForTable(tableDesc, mappings);
+            updated = reinit(updated, TableMetadataManager.TABLE_SERIALIZER);
+
+            try (DataInputStream bis = new DataInputStream(new FileInputStream(
+                    new File(mappingRootPath + TableDesc.concatResourcePath(updated.getIdentity(), null))))) {
+                TableDesc expected = TableMetadataManager.TABLE_SERIALIZER.deserialize(bis);
+                Assert.assertEquals(expected, updated);
+            } catch (Exception e) {
+                Assert.fail("Table identity is not updated correctly");
+            }
+        }
+    }
+
+    @Test
+    public void testDealWithMappingForModel() throws IOException {
+        DataModelManager dataModelManager = DataModelManager.getInstance(getTestConfig());
+        DataModelDesc model = dataModelManager.getDataModelDesc("ci_inner_join_model");
+
+        DataModelDesc updated = TableSchemaUpdater.dealWithMappingForModel(model, mappings);
+        updated = reinit(updated, dataModelManager.getDataModelSerializer());
+
+        try (DataInputStream bis = new DataInputStream(
+                new FileInputStream(new File(mappingRootPath + DataModelDesc.concatResourcePath(updated.getName()))))) {
+            DataModelDesc expected = dataModelManager.getDataModelSerializer().deserialize(bis);
+            Assert.assertTrue(expected.equalsRaw(updated));
+        } catch (Exception e) {
+            Assert.fail("Model is not updated correctly");
+        }
+    }
+
+    @Test
+    public void testDealWithMappingForCubeDesc() throws IOException {
+        CubeDescManager cubeDescManager = CubeDescManager.getInstance(getTestConfig());
+        CubeDesc cubeDesc = cubeDescManager.getCubeDesc("ci_left_join_cube");
+
+        CubeDesc updated = TableSchemaUpdater.dealWithMappingForCubeDesc(cubeDesc, mappings);
+        updated = reinit(updated, cubeDescManager.CUBE_DESC_SERIALIZER);
+
+        try (DataInputStream bis = new DataInputStream(
+                new FileInputStream(new File(mappingRootPath + CubeDesc.concatResourcePath(updated.getName()))))) {
+            CubeDesc expected = cubeDescManager.CUBE_DESC_SERIALIZER.deserialize(bis);
+            Assert.assertTrue(expected.equalsRaw(updated));
+        } catch (Exception e) {
+            Assert.fail("CubeDesc is not updated correctly");
+        }
+    }
+
+    @Test
+    public void testDealWithMappingForCube() throws IOException {
+        CubeManager cubeManager = CubeManager.getInstance(getTestConfig());
+        CubeInstance cube = cubeManager.getCube("test_kylin_cube_with_slr_left_join_ready");
+
+        CubeInstance updated = TableSchemaUpdater.dealWithMappingForCube(cube, mappings);
+        updated = reinit(updated, cubeManager.CUBE_SERIALIZER);
+
+        try (DataInputStream bis = new DataInputStream(
+                new FileInputStream(new File(mappingRootPath + CubeInstance.concatResourcePath(updated.getName()))))) {
+            CubeInstance expected = cubeManager.CUBE_SERIALIZER.deserialize(bis);
+            Assert.assertTrue(expected.equalsRaw(updated));
+        } catch (Exception e) {
+            Assert.fail("CubeInstance is not updated correctly");
+        }
+    }
+
+    private <T extends RootPersistentEntity> T reinit(T obj, Serializer<T> serializer) throws IOException {
+        ByteArrayOutputStream baos = new ByteArrayOutputStream();
+        DataOutputStream dos = new DataOutputStream(baos);
+        serializer.serialize(obj, dos);
+        ByteArrayInputStream bais = new ByteArrayInputStream(baos.toByteArray());
+        DataInputStream dis = new DataInputStream(bais);
+        return serializer.deserialize(dis);
+    }
+}
diff --git a/server-base/src/test/resources/update/TableSchemaUpdateMapping.json b/server-base/src/test/resources/update/TableSchemaUpdateMapping.json
new file mode 100644
index 0000000..bd601c8
--- /dev/null
+++ b/server-base/src/test/resources/update/TableSchemaUpdateMapping.json
@@ -0,0 +1,19 @@
+{
+  "DEFAULT.TEST_KYLIN_FACT": {
+    "database": "TEST",
+    "tableName": "KYLIN_FACT"
+  },
+  "DEFAULT.TEST_ACCOUNT": {
+    "database": "TEST"
+  },
+  "DEFAULT.TEST_COUNTRY": {
+    "database": "TEST",
+    "tableName": "COUNTRY"
+  },
+  "EDW.TEST_CAL_DT": {
+    "tableName": "CAL_DT"
+  },
+  "DEFAULT.TEST_CATEGORY_GROUPINGS": {
+    "database": "TEST"
+  }
+}
\ No newline at end of file
diff --git a/server-base/src/test/resources/update/cube/test_kylin_cube_with_slr_left_join_ready.json b/server-base/src/test/resources/update/cube/test_kylin_cube_with_slr_left_join_ready.json
new file mode 100644
index 0000000..8cd7548
--- /dev/null
+++ b/server-base/src/test/resources/update/cube/test_kylin_cube_with_slr_left_join_ready.json
@@ -0,0 +1,44 @@
+{
+  "uuid" : "kkkka32a-a33e-4b69-83dd-0bb8b1f8c53b",
+ 
+  "last_modified" : 1404097095621,
+  "name" : "test_kylin_cube_with_slr_left_join_ready",
+  "owner" : null,
+  "descriptor" : "test_kylin_cube_with_slr_left_join_desc",
+  "cost" : 50,
+  "segments" : [ {
+    "uuid" : "1eaca32a-a33e-4b69-83dd-xxe8b1f8dddd",
+	"name" : "1eaca32a-a33e-4b69-83dd-xxe8b1f8dddd",
+	"storage_location_identifier" : "KYLIN-CUBE-TEST_KYLIN_CUBE_WITH_SLR_LEFT_JOIN_READY-BCF2F125-9B0B-40DD-9509-95EC59B31333",
+    "date_range_start" : 0,
+    "date_range_end" : 1384243200000,
+	"status" : "READY",
+	"size_kb" : 7690,
+	"source_records" : 10000,
+	"source_records_size" : 610288,
+	"last_build_time" : 1404097095455,
+	"last_build_job_id" : "bcf2f125-9b0b-40dd-9509-95ec59b31333",
+	"binary_signature" : null,
+      "dictionaries" : {
+        "TEST.TEST_CATEGORY_GROUPINGS/CATEG_LVL2_NAME" : "/dict/TEST_CATEGORY_GROUPINGS/CATEG_LVL2_NAME/c12ae49d-9dbe-4a58-b169-19afac317696.dict",
+        "TEST.TEST_FACT/SLR_SEGMENT_CD" : "/dict/TEST_SELLER_TYPE_DIM/SELLER_TYPE_CD/14fe66b3-5956-498c-bd93-40182cac5510.dict",
+        "TEST.TEST_FACT/LSTG_SITE_ID" : "/dict/TEST_SITES/SITE_ID/0bec6bb3-1b0d-469c-8289-b8c4ca5d5001.dict",
+        "EDW.TEST_SELLER_TYPE_DIM/SELLER_TYPE_CD" : "/dict/TEST_SELLER_TYPE_DIM/SELLER_TYPE_CD/2a44ff38-f64b-42e7-9fcf-66afccac8047.dict",
+        "TEST.TEST_FACT/CAL_DT" : "/dict/PREDEFINED/date(yyyy-mm-dd)/64ac4f82-f2af-476e-85b9-f0805001014e.dict",
+        "TEST.TEST_CATEGORY_GROUPINGS/CATEG_LVL3_NAME" : "/dict/TEST_CATEGORY_GROUPINGS/CATEG_LVL3_NAME/eacccee7-d120-4f4d-97d0-c99a5b83ec32.dict",
+        "EDW.CAL_DT/CAL_DT" : "/dict/TEST_CAL_DT/CAL_DT/ed0c3451-593c-494c-9019-64f63fcb0b8e.dict",
+        "TEST.TEST_FACT/LEAF_CATEG_ID" : "/dict/TEST_CATEGORY_GROUPINGS/LEAF_CATEG_ID/8b4b1c06-fb74-486b-a2ad-74420afebcda.dict",
+        "EDW.TEST_SITES/SITE_ID" : "/dict/TEST_SITES/SITE_ID/ff7e8943-ac0f-4e66-b9ed-510f6a0b875d.dict",
+        "TEST.TEST_CATEGORY_GROUPINGS/META_CATEG_NAME" : "/dict/TEST_CATEGORY_GROUPINGS/META_CATEG_NAME/c2af25cf-6c79-45e6-a6f6-6d2a8ecc6592.dict"
+      },
+      "snapshots" : {
+        "EDW.TEST_SELLER_TYPE_DIM" : "/table_snapshot/TEST_SELLER_TYPE_DIM.csv/b43dd3f1-9a34-4721-8abc-df90962e94d8.snapshot",
+        "EDW.CAL_DT" : "/table_snapshot/TEST_CAL_DT.csv/4af48c94-86de-4e22-a4fd-c49b06cbaa4f.snapshot",
+        "TEST.TEST_CATEGORY_GROUPINGS" : "/table_snapshot/TEST_CATEGORY_GROUPINGS.csv/89715f33-15c9-4745-83f9-f2b9817d9100.snapshot",
+        "EDW.TEST_SITES" : "/table_snapshot/TEST_SITES.csv/7d70b82e-43f6-4999-b012-0e91a9bb8408.snapshot"
+      }
+  } ],
+  "status" : "READY",
+  "create_time" : null,
+  "notify_list" : null
+}
\ No newline at end of file
diff --git a/server-base/src/test/resources/update/cube_desc/ci_left_join_cube.json b/server-base/src/test/resources/update/cube_desc/ci_left_join_cube.json
new file mode 100644
index 0000000..f7f5959
--- /dev/null
+++ b/server-base/src/test/resources/update/cube_desc/ci_left_join_cube.json
@@ -0,0 +1,619 @@
+{
+  "uuid": "629ab7a8-3929-4dff-b59d-2100aadccd1a",
+  "name": "ci_left_join_cube",
+  "model_name": "ci_left_join_model",
+  "description": null,
+  "dimensions": [
+    {
+      "name": "CAL_DT",
+      "table": "TEST_CAL_DT",
+      "column": "{FK}",
+      "derived": [
+        "WEEK_BEG_DT"
+      ]
+    },
+    {
+      "name": "ORDER_ID",
+      "table": "TEST_KYLIN_FACT",
+      "column": "ORDER_ID"
+    },
+    {
+      "name": "TEST_DATE_ENC",
+      "table": "TEST_ORDER",
+      "column": "TEST_DATE_ENC"
+    },
+    {
+      "name": "TEST_TIME_ENC",
+      "table": "TEST_ORDER",
+      "column": "TEST_TIME_ENC"
+    },
+    {
+      "name": "CATEGORY",
+      "table": "TEST_CATEGORY_GROUPINGS",
+      "column": "{FK}",
+      "derived": [
+        "USER_DEFINED_FIELD1",
+        "USER_DEFINED_FIELD3",
+        "UPD_DATE",
+        "UPD_USER"
+      ]
+    },
+    {
+      "name": "CATEGORY_HIERARCHY",
+      "table": "TEST_CATEGORY_GROUPINGS",
+      "column": "META_CATEG_NAME",
+      "derived": null
+    },
+    {
+      "name": "CATEGORY_HIERARCHY",
+      "table": "TEST_CATEGORY_GROUPINGS",
+      "column": "CATEG_LVL2_NAME",
+      "derived": null
+    },
+    {
+      "name": "CATEGORY_HIERARCHY",
+      "table": "TEST_CATEGORY_GROUPINGS",
+      "column": "CATEG_LVL3_NAME",
+      "derived": null
+    },
+    {
+      "name": "LSTG_FORMAT_NAME",
+      "table": "TEST_KYLIN_FACT",
+      "column": "LSTG_FORMAT_NAME",
+      "derived": null
+    },
+    {
+      "name": "SITE_ID",
+      "table": "TEST_SITES",
+      "column": "{FK}",
+      "derived": [
+        "SITE_NAME",
+        "CRE_USER"
+      ]
+    },
+    {
+      "name": "SELLER_TYPE_CD",
+      "table": "TEST_SELLER_TYPE_DIM",
+      "column": "{FK}",
+      "derived": [
+        "SELLER_TYPE_DESC"
+      ]
+    },
+    {
+      "name": "SELLER_ID",
+      "table": "TEST_KYLIN_FACT",
+      "column": "SELLER_ID"
+    },
+    {
+      "name": "SELLER_BUYER_LEVEL",
+      "table": "SELLER_ACCOUNT",
+      "column": "ACCOUNT_BUYER_LEVEL"
+    },
+    {
+      "name": "SELLER_SELLER_LEVEL",
+      "table": "SELLER_ACCOUNT",
+      "column": "ACCOUNT_SELLER_LEVEL"
+    },
+    {
+      "name": "SELLER_COUNTRY",
+      "table": "SELLER_ACCOUNT",
+      "column": "ACCOUNT_COUNTRY"
+    },
+    {
+      "name": "SELLER_COUNTRY_NAME",
+      "table": "SELLER_COUNTRY",
+      "column": "NAME"
+    },
+    {
+      "name": "BUYER_ID",
+      "table": "TEST_ORDER",
+      "column": "BUYER_ID"
+    },
+    {
+      "name": "BUYER_BUYER_LEVEL",
+      "table": "BUYER_ACCOUNT",
+      "column": "ACCOUNT_BUYER_LEVEL"
+    },
+    {
+      "name": "BUYER_SELLER_LEVEL",
+      "table": "BUYER_ACCOUNT",
+      "column": "ACCOUNT_SELLER_LEVEL"
+    },
+    {
+      "name": "BUYER_COUNTRY",
+      "table": "BUYER_ACCOUNT",
+      "column": "ACCOUNT_COUNTRY"
+    },
+    {
+      "name": "BUYER_COUNTRY_NAME",
+      "table": "BUYER_COUNTRY",
+      "column": "NAME"
+    }
+  ],
+  "measures": [
+    {
+      "name": "TRANS_CNT",
+      "function": {
+        "expression": "COUNT",
+        "parameter": {
+          "type": "constant",
+          "value": "1"
+        },
+        "returntype": "bigint"
+      }
+    },
+    {
+      "name": "ITEM_COUNT_SUM",
+      "function": {
+        "expression": "SUM",
+        "parameter": {
+          "type": "column",
+          "value": "TEST_KYLIN_FACT.ITEM_COUNT"
+        },
+        "returntype": "bigint"
+      }
+    },
+    {
+      "name": "GMV_SUM",
+      "function": {
+        "expression": "SUM",
+        "parameter": {
+          "type": "column",
+          "value": "TEST_KYLIN_FACT.PRICE"
+        },
+        "returntype": "decimal(19,4)"
+      }
+    },
+    {
+      "name": "GMV_CNT",
+      "function": {
+        "expression": "COUNT",
+        "parameter": {
+          "type": "column",
+          "value": "TEST_KYLIN_FACT.PRICE"
+        },
+        "returntype": "bigint"
+      }
+    },
+    {
+      "name": "GMV_MIN",
+      "function": {
+        "expression": "MIN",
+        "parameter": {
+          "type": "column",
+          "value": "TEST_KYLIN_FACT.PRICE"
+        },
+        "returntype": "decimal(19,4)"
+      }
+    },
+    {
+      "name": "GMV_MAX",
+      "function": {
+        "expression": "MAX",
+        "parameter": {
+          "type": "column",
+          "value": "TEST_KYLIN_FACT.PRICE"
+        },
+        "returntype": "decimal(19,4)"
+      }
+    },
+    {
+      "name": "SELLER_HLL",
+      "function": {
+        "expression": "COUNT_DISTINCT",
+        "parameter": {
+          "type": "column",
+          "value": "TEST_KYLIN_FACT.SELLER_ID"
+        },
+        "returntype": "hllc(10)"
+      }
+    },
+    {
+      "name": "SELLER_FORMAT_HLL",
+      "function": {
+        "expression": "COUNT_DISTINCT",
+        "parameter": {
+          "type": "column",
+          "value": "TEST_KYLIN_FACT.LSTG_FORMAT_NAME",
+          "next_parameter": {
+            "type": "column",
+            "value": "TEST_KYLIN_FACT.SELLER_ID"
+          }
+        },
+        "returntype": "hllc(10)"
+      }
+    },
+    {
+      "name": "TOP_SELLER",
+      "function": {
+        "expression": "TOP_N",
+        "parameter": {
+          "type": "column",
+          "value": "TEST_KYLIN_FACT.PRICE",
+          "next_parameter": {
+            "type": "column",
+            "value": "TEST_KYLIN_FACT.SELLER_ID"
+          }
+        },
+        "returntype": "topn(100, 4)",
+        "configuration": {
+          "topn.encoding.TEST_KYLIN_FACT.SELLER_ID": "int:4"
+        }
+      }
+    },
+    {
+      "name": "TEST_COUNT_DISTINCT_BITMAP",
+      "function": {
+        "expression": "COUNT_DISTINCT",
+        "parameter": {
+          "type": "column",
+          "value": "TEST_KYLIN_FACT.TEST_COUNT_DISTINCT_BITMAP"
+        },
+        "returntype": "bitmap"
+      }
+    },
+    {
+      "name": "TEST_EXTENDED_COLUMN",
+      "function": {
+        "expression": "EXTENDED_COLUMN",
+        "parameter": {
+          "type": "column",
+          "value": "TEST_KYLIN_FACT.ORDER_ID",
+          "next_parameter": {
+            "type": "column",
+            "value": "TEST_ORDER.TEST_EXTENDED_COLUMN"
+          }
+        },
+        "returntype": "extendedcolumn(100)"
+      }
+    },
+    {
+      "name": "BUYER_CONTACT",
+      "function": {
+        "expression": "EXTENDED_COLUMN",
+        "parameter": {
+          "type": "column",
+          "value": "TEST_ORDER.BUYER_ID",
+          "next_parameter": {
+            "type": "column",
+            "value": "BUYER_ACCOUNT.ACCOUNT_CONTACT"
+          }
+        },
+        "returntype": "extendedcolumn(100)"
+      }
+    },
+    {
+      "name": "SELLER_CONTACT",
+      "function": {
+        "expression": "EXTENDED_COLUMN",
+        "parameter": {
+          "type": "column",
+          "value": "TEST_KYLIN_FACT.SELLER_ID",
+          "next_parameter": {
+            "type": "column",
+            "value": "SELLER_ACCOUNT.ACCOUNT_CONTACT"
+          }
+        },
+        "returntype": "extendedcolumn(100)"
+      }
+    },
+    {
+      "name": "TRANS_ID_RAW",
+      "function": {
+        "expression": "RAW",
+        "parameter": {
+          "type": "column",
+          "value": "TEST_KYLIN_FACT.TRANS_ID"
+        },
+        "returntype": "raw"
+      }
+    },
+    {
+      "name": "PRICE_RAW",
+      "function": {
+        "expression": "RAW",
+        "parameter": {
+          "type": "column",
+          "value": "TEST_KYLIN_FACT.PRICE"
+        },
+        "returntype": "raw"
+      }
+    },
+    {
+      "name": "CAL_DT_RAW",
+      "function": {
+        "expression": "RAW",
+        "parameter": {
+          "type": "column",
+          "value": "TEST_KYLIN_FACT.CAL_DT"
+        },
+        "returntype": "raw"
+      }
+    },
+    {
+      "name": "GVM_PERCENTILE",
+      "function": {
+        "expression": "PERCENTILE_APPROX",
+        "parameter": {
+          "type": "column",
+          "value": "TEST_KYLIN_FACT.PRICE"
+        },
+        "returntype": "percentile(100)"
+      }
+    },
+    {
+      "name": "TEST_COUNT_COLUMN_CNT",
+      "function": {
+        "expression": "COUNT",
+        "parameter": {
+          "type": "column",
+          "value": "TEST_KYLIN_FACT.TEST_COUNT_COLUMN"
+        },
+        "returntype": "bigint"
+      }
+    }
+  ],
+  "dictionaries": [
+    {
+      "column": "TEST_KYLIN_FACT.TEST_COUNT_DISTINCT_BITMAP",
+      "builder": "org.apache.kylin.dict.GlobalDictionaryBuilder"
+    }
+  ],
+  "rowkey": {
+    "rowkey_columns": [
+      {
+        "column": "TEST_KYLIN_FACT.SELLER_ID",
+        "encoding": "int:4"
+      },
+      {
+        "column": "TEST_KYLIN_FACT.ORDER_ID",
+        "encoding": "int:4"
+      },
+      {
+        "column": "TEST_KYLIN_FACT.CAL_DT",
+        "encoding": "dict"
+      },
+      {
+        "column": "TEST_KYLIN_FACT.LEAF_CATEG_ID",
+        "encoding": "dict"
+      },
+      {
+        "column": "TEST_CATEGORY_GROUPINGS.META_CATEG_NAME",
+        "encoding": "dict"
+      },
+      {
+        "column": "TEST_CATEGORY_GROUPINGS.CATEG_LVL2_NAME",
+        "encoding": "dict"
+      },
+      {
+        "column": "TEST_CATEGORY_GROUPINGS.CATEG_LVL3_NAME",
+        "encoding": "dict"
+      },
+      {
+        "column": "TEST_KYLIN_FACT.LSTG_FORMAT_NAME",
+        "encoding": "fixed_length:12"
+      },
+      {
+        "column": "TEST_KYLIN_FACT.LSTG_SITE_ID",
+        "encoding": "dict"
+      },
+      {
+        "column": "TEST_KYLIN_FACT.SLR_SEGMENT_CD",
+        "encoding": "dict"
+      },
+      {
+        "column": "TEST_ORDER.TEST_TIME_ENC",
+        "encoding": "time"
+      },
+      {
+        "column": "TEST_ORDER.TEST_DATE_ENC",
+        "encoding": "date"
+      },
+      {
+        "column": "TEST_ORDER.BUYER_ID",
+        "encoding": "int:4"
+      },
+      {
+        "column": "BUYER_ACCOUNT.ACCOUNT_BUYER_LEVEL",
+        "encoding": "dict"
+      },
+      {
+        "column": "BUYER_ACCOUNT.ACCOUNT_SELLER_LEVEL",
+        "encoding": "dict"
+      },
+      {
+        "column": "BUYER_ACCOUNT.ACCOUNT_COUNTRY",
+        "encoding": "dict"
+      },
+      {
+        "column": "BUYER_COUNTRY.NAME",
+        "encoding": "dict"
+      },
+      {
+        "column": "SELLER_ACCOUNT.ACCOUNT_BUYER_LEVEL",
+        "encoding": "dict"
+      },
+      {
+        "column": "SELLER_ACCOUNT.ACCOUNT_SELLER_LEVEL",
+        "encoding": "dict"
+      },
+      {
+        "column": "SELLER_ACCOUNT.ACCOUNT_COUNTRY",
+        "encoding": "dict"
+      },
+      {
+        "column": "SELLER_COUNTRY.NAME",
+        "encoding": "dict"
+      }
+    ]
+  },
+  "signature": null,
+  "last_modified": 1448959801311,
+  "null_string": null,
+  "hbase_mapping": {
+    "column_family": [
+      {
+        "name": "F1",
+        "columns": [
+          {
+            "qualifier": "M",
+            "measure_refs": [
+              "TRANS_CNT",
+              "ITEM_COUNT_SUM",
+              "GMV_SUM",
+              "GMV_CNT",
+              "GMV_MIN",
+              "GMV_MAX",
+              "TEST_COUNT_COLUMN_CNT"
+            ]
+          }
+        ]
+      },
+      {
+        "name": "F2",
+        "columns": [
+          {
+            "qualifier": "M",
+            "measure_refs": [
+              "SELLER_HLL",
+              "SELLER_FORMAT_HLL",
+              "TOP_SELLER",
+              "TEST_COUNT_DISTINCT_BITMAP"
+            ]
+          }
+        ]
+      },
+      {
+        "name": "F3",
+        "columns": [
+          {
+            "qualifier": "M",
+            "measure_refs": [
+              "TEST_EXTENDED_COLUMN",
+              "BUYER_CONTACT",
+              "SELLER_CONTACT",
+              "TRANS_ID_RAW",
+              "PRICE_RAW",
+              "CAL_DT_RAW",
+              "GVM_PERCENTILE"
+            ]
+          }
+        ]
+      }
+    ]
+  },
+  "aggregation_groups": [
+    {
+      "includes": [
+        "TEST_KYLIN_FACT.CAL_DT",
+        "TEST_KYLIN_FACT.LEAF_CATEG_ID",
+        "TEST_KYLIN_FACT.LSTG_FORMAT_NAME",
+        "TEST_KYLIN_FACT.LSTG_SITE_ID",
+        "TEST_KYLIN_FACT.SLR_SEGMENT_CD",
+        "TEST_CATEGORY_GROUPINGS.META_CATEG_NAME",
+        "TEST_CATEGORY_GROUPINGS.CATEG_LVL2_NAME",
+        "TEST_CATEGORY_GROUPINGS.CATEG_LVL3_NAME"
+      ],
+      "select_rule": {
+        "hierarchy_dims": [
+          [
+            "TEST_CATEGORY_GROUPINGS.META_CATEG_NAME",
+            "TEST_CATEGORY_GROUPINGS.CATEG_LVL2_NAME",
+            "TEST_CATEGORY_GROUPINGS.CATEG_LVL3_NAME",
+            "TEST_KYLIN_FACT.LEAF_CATEG_ID"
+          ]
+        ],
+        "mandatory_dims": [],
+        "joint_dims": [
+          [
+            "TEST_KYLIN_FACT.LSTG_FORMAT_NAME",
+            "TEST_KYLIN_FACT.LSTG_SITE_ID",
+            "TEST_KYLIN_FACT.SLR_SEGMENT_CD"
+          ]
+        ],
+        "dim_cap": 5
+      }
+    },
+    {
+      "includes": [
+        "TEST_KYLIN_FACT.CAL_DT",
+        "TEST_KYLIN_FACT.LEAF_CATEG_ID",
+        "TEST_KYLIN_FACT.LSTG_FORMAT_NAME",
+        "TEST_KYLIN_FACT.LSTG_SITE_ID",
+        "TEST_KYLIN_FACT.SLR_SEGMENT_CD",
+        "TEST_CATEGORY_GROUPINGS.META_CATEG_NAME",
+        "TEST_CATEGORY_GROUPINGS.CATEG_LVL2_NAME",
+        "TEST_CATEGORY_GROUPINGS.CATEG_LVL3_NAME",
+        "TEST_KYLIN_FACT.SELLER_ID",
+        "SELLER_ACCOUNT.ACCOUNT_BUYER_LEVEL",
+        "SELLER_ACCOUNT.ACCOUNT_SELLER_LEVEL",
+        "SELLER_ACCOUNT.ACCOUNT_COUNTRY",
+        "SELLER_COUNTRY.NAME",
+        "TEST_KYLIN_FACT.ORDER_ID",
+        "TEST_ORDER.TEST_DATE_ENC",
+        "TEST_ORDER.TEST_TIME_ENC",
+        "TEST_ORDER.BUYER_ID",
+        "BUYER_ACCOUNT.ACCOUNT_BUYER_LEVEL",
+        "BUYER_ACCOUNT.ACCOUNT_SELLER_LEVEL",
+        "BUYER_ACCOUNT.ACCOUNT_COUNTRY",
+        "BUYER_COUNTRY.NAME"
+      ],
+      "select_rule": {
+        "hierarchy_dims": [],
+        "mandatory_dims": [
+          "TEST_KYLIN_FACT.CAL_DT"
+        ],
+        "joint_dims": [
+          [
+            "TEST_CATEGORY_GROUPINGS.META_CATEG_NAME",
+            "TEST_CATEGORY_GROUPINGS.CATEG_LVL2_NAME",
+            "TEST_CATEGORY_GROUPINGS.CATEG_LVL3_NAME",
+            "TEST_KYLIN_FACT.LEAF_CATEG_ID"
+          ],
+          [
+            "TEST_KYLIN_FACT.LSTG_FORMAT_NAME",
+            "TEST_KYLIN_FACT.LSTG_SITE_ID",
+            "TEST_KYLIN_FACT.SLR_SEGMENT_CD"
+          ],
+          [
+            "TEST_KYLIN_FACT.SELLER_ID",
+            "SELLER_ACCOUNT.ACCOUNT_BUYER_LEVEL",
+            "SELLER_ACCOUNT.ACCOUNT_SELLER_LEVEL",
+            "SELLER_ACCOUNT.ACCOUNT_COUNTRY",
+            "SELLER_COUNTRY.NAME"
+          ],
+          [
+            "TEST_KYLIN_FACT.ORDER_ID",
+            "TEST_ORDER.TEST_DATE_ENC",
+            "TEST_ORDER.TEST_TIME_ENC",
+            "TEST_ORDER.BUYER_ID",
+            "BUYER_ACCOUNT.ACCOUNT_BUYER_LEVEL",
+            "BUYER_ACCOUNT.ACCOUNT_SELLER_LEVEL",
+            "BUYER_ACCOUNT.ACCOUNT_COUNTRY",
+            "BUYER_COUNTRY.NAME"
+          ]
+        ],
+        "dim_cap": 3
+      }
+    }
+  ],
+  "notify_list": null,
+  "status_need_notify": [],
+  "auto_merge_time_ranges": null,
+  "retention_range": 0,
+  "engine_type": 2,
+  "storage_type": 2,
+  "override_kylin_properties": {
+    "kylin.cube.algorithm": "INMEM",
+    "kylin.dictionary.shrunken-from-global-enabled": "true"
+  },
+  "snapshot_table_desc_list": [
+    {
+      "table_name": "TEST.TEST_CATEGORY_GROUPINGS",
+      "storage_type": "hbase",
+      "local_cache_enable": true,
+      "global": true
+    }
+  ],
+  "partition_date_start": 0
+}
diff --git a/server-base/src/test/resources/update/model_desc/ci_inner_join_model.json b/server-base/src/test/resources/update/model_desc/ci_inner_join_model.json
new file mode 100644
index 0000000..8a74b36
--- /dev/null
+++ b/server-base/src/test/resources/update/model_desc/ci_inner_join_model.json
@@ -0,0 +1,238 @@
+{
+  "uuid": "72ab4ee2-2cdb-4b07-b39e-4c298563ae27",
+  "name": "ci_inner_join_model",
+  "fact_table": "TEST.KYLIN_FACT",
+  "fact_table_alias": "TEST_KYLIN_FACT",
+  "lookups": [
+    {
+      "table": "DEFAULT.TEST_ORDER",
+      "alias": "TEST_ORDER",
+      "kind": "FACT",
+      "join": {
+        "type": "INNER",
+        "primary_key": [
+          "TEST_ORDER.ORDER_ID"
+        ],
+        "foreign_key": [
+          "TEST_KYLIN_FACT.ORDER_ID"
+        ]
+      }
+    },
+    {
+      "table": "TEST.TEST_ACCOUNT",
+      "alias": "BUYER_ACCOUNT",
+      "kind": "FACT",
+      "join": {
+        "type": "INNER",
+        "primary_key": [
+          "BUYER_ACCOUNT.ACCOUNT_ID"
+        ],
+        "foreign_key": [
+          "TEST_ORDER.BUYER_ID"
+        ]
+      }
+    },
+    {
+      "table": "EDW.CAL_DT",
+      "alias": "TEST_CAL_DT",
+      "join": {
+        "type": "INNER",
+        "primary_key": [
+          "TEST_CAL_DT.CAL_DT"
+        ],
+        "foreign_key": [
+          "TEST_KYLIN_FACT.CAL_DT"
+        ]
+      }
+    },
+    {
+      "table": "TEST.TEST_CATEGORY_GROUPINGS",
+      "alias": "TEST_CATEGORY_GROUPINGS",
+      "join": {
+        "type": "INNER",
+        "primary_key": [
+          "TEST_CATEGORY_GROUPINGS.LEAF_CATEG_ID",
+          "TEST_CATEGORY_GROUPINGS.SITE_ID"
+        ],
+        "foreign_key": [
+          "TEST_KYLIN_FACT.LEAF_CATEG_ID",
+          "TEST_KYLIN_FACT.LSTG_SITE_ID"
+        ]
+      }
+    },
+    {
+      "table": "EDW.TEST_SITES",
+      "alias": "TEST_SITES",
+      "join": {
+        "type": "INNER",
+        "primary_key": [
+          "TEST_SITES.SITE_ID"
+        ],
+        "foreign_key": [
+          "TEST_KYLIN_FACT.LSTG_SITE_ID"
+        ]
+      }
+    },
+    {
+      "table": "EDW.TEST_SELLER_TYPE_DIM",
+      "alias": "TEST_SELLER_TYPE_DIM",
+      "join": {
+        "type": "INNER",
+        "primary_key": [
+          "TEST_SELLER_TYPE_DIM.SELLER_TYPE_CD"
+        ],
+        "foreign_key": [
+          "TEST_KYLIN_FACT.SLR_SEGMENT_CD"
+        ]
+      }
+    },
+    {
+      "table": "TEST.COUNTRY",
+      "alias": "BUYER_COUNTRY",
+      "join": {
+        "type": "INNER",
+        "primary_key": [
+          "BUYER_COUNTRY.COUNTRY"
+        ],
+        "foreign_key": [
+          "BUYER_ACCOUNT.ACCOUNT_COUNTRY"
+        ]
+      }
+    },
+    {
+      "table": "TEST.COUNTRY",
+      "alias": "SELLER_COUNTRY",
+      "join": {
+        "type": "INNER",
+        "primary_key": [
+          "SELLER_COUNTRY.COUNTRY"
+        ],
+        "foreign_key": [
+          "SELLER_ACCOUNT.ACCOUNT_COUNTRY"
+        ]
+      }
+    },
+    {
+      "table": "TEST.TEST_ACCOUNT",
+      "alias": "SELLER_ACCOUNT",
+      "kind": "FACT",
+      "join": {
+        "type": "INNER",
+        "primary_key": [
+          "SELLER_ACCOUNT.ACCOUNT_ID"
+        ],
+        "foreign_key": [
+          "TEST_KYLIN_FACT.SELLER_ID"
+        ]
+      }
+    }
+  ],
+  "dimensions": [
+    {
+      "table": "TEST_KYLIN_FACT",
+      "columns": [
+        "TRANS_ID",
+        "ORDER_ID",
+        "CAL_DT",
+        "LSTG_FORMAT_NAME",
+        "LSTG_SITE_ID",
+        "LEAF_CATEG_ID",
+        "SLR_SEGMENT_CD",
+        "SELLER_ID",
+        "TEST_COUNT_DISTINCT_BITMAP"
+      ]
+    },
+    {
+      "table": "TEST_ORDER",
+      "columns": [
+        "ORDER_ID",
+        "BUYER_ID",
+        "TEST_DATE_ENC",
+        "TEST_TIME_ENC",
+        "TEST_EXTENDED_COLUMN"
+      ]
+    },
+    {
+      "table": "BUYER_ACCOUNT",
+      "columns": [
+        "ACCOUNT_ID",
+        "ACCOUNT_BUYER_LEVEL",
+        "ACCOUNT_SELLER_LEVEL",
+        "ACCOUNT_COUNTRY",
+        "ACCOUNT_CONTACT"
+      ]
+    },
+    {
+      "table": "SELLER_ACCOUNT",
+      "columns": [
+        "ACCOUNT_ID",
+        "ACCOUNT_BUYER_LEVEL",
+        "ACCOUNT_SELLER_LEVEL",
+        "ACCOUNT_COUNTRY",
+        "ACCOUNT_CONTACT"
+      ]
+    },
+    {
+      "table": "TEST_CATEGORY_GROUPINGS",
+      "columns": [
+        "LEAF_CATEG_ID",
+        "SITE_ID",
+        "META_CATEG_NAME",
+        "CATEG_LVL2_NAME",
+        "CATEG_LVL3_NAME",
+        "USER_DEFINED_FIELD1",
+        "USER_DEFINED_FIELD3",
+        "UPD_DATE",
+        "UPD_USER"
+      ]
+    },
+    {
+      "table": "TEST_SITES",
+      "columns": [
+        "SITE_ID",
+        "SITE_NAME",
+        "CRE_USER"
+      ]
+    },
+    {
+      "table": "TEST_SELLER_TYPE_DIM",
+      "columns": [
+        "SELLER_TYPE_CD",
+        "SELLER_TYPE_DESC"
+      ]
+    },
+    {
+      "table": "TEST_CAL_DT",
+      "columns": [
+        "CAL_DT",
+        "WEEK_BEG_DT"
+      ]
+    },
+    {
+      "table": "BUYER_COUNTRY",
+      "columns": [
+        "COUNTRY",
+        "NAME"
+      ]
+    },
+    {
+      "table": "SELLER_COUNTRY",
+      "columns": [
+        "COUNTRY",
+        "NAME"
+      ]
+    }
+  ],
+  "metrics": [
+    "TEST_KYLIN_FACT.TEST_COUNT_COLUMN",
+    "TEST_KYLIN_FACT.PRICE",
+    "TEST_KYLIN_FACT.ITEM_COUNT"
+  ],
+  "last_modified": 1422435345352,
+  "filter_condition": null,
+  "partition_desc": {
+    "partition_date_column": "TEST.KYLIN_FACT.CAL_DT",
+    "partition_date_start": 0,
+    "partition_type": "APPEND"
+  }
+}
diff --git a/server-base/src/test/resources/update/table/EDW.CAL_DT.json b/server-base/src/test/resources/update/table/EDW.CAL_DT.json
new file mode 100644
index 0000000..8cbcec8
--- /dev/null
+++ b/server-base/src/test/resources/update/table/EDW.CAL_DT.json
@@ -0,0 +1,413 @@
+{
+ 
+  "uuid" : "0ff420eb-79ad-40bd-bca9-12d8cd05c60a",
+  "name" : "CAL_DT",
+  "columns" : [ {
+    "id" : "1",
+    "name" : "CAL_DT",
+    "datatype" : "date",
+    "index": "T"
+  }, {
+    "id" : "2",
+    "name" : "YEAR_BEG_DT",
+    "datatype" : "date",
+    "index": "T"
+  }, {
+    "id" : "3",
+    "name" : "QTR_BEG_DT",
+    "datatype" : "date",
+    "index": "T"
+  }, {
+    "id" : "4",
+    "name" : "MONTH_BEG_DT",
+    "datatype" : "date",
+    "index": "T"
+  }, {
+    "id" : "5",
+    "name" : "WEEK_BEG_DT",
+    "datatype" : "date",
+    "index": "T"
+  }, {
+    "id" : "6",
+    "name" : "AGE_FOR_YEAR_ID",
+    "datatype" : "smallint"
+  }, {
+    "id" : "7",
+    "name" : "AGE_FOR_QTR_ID",
+    "datatype" : "smallint"
+  }, {
+    "id" : "8",
+    "name" : "AGE_FOR_MONTH_ID",
+    "datatype" : "smallint"
+  }, {
+    "id" : "9",
+    "name" : "AGE_FOR_WEEK_ID",
+    "datatype" : "smallint"
+  }, {
+    "id" : "10",
+    "name" : "AGE_FOR_DT_ID",
+    "datatype" : "smallint"
+  }, {
+    "id" : "11",
+    "name" : "AGE_FOR_RTL_YEAR_ID",
+    "datatype" : "smallint"
+  }, {
+    "id" : "12",
+    "name" : "AGE_FOR_RTL_QTR_ID",
+    "datatype" : "smallint"
+  }, {
+    "id" : "13",
+    "name" : "AGE_FOR_RTL_MONTH_ID",
+    "datatype" : "smallint"
+  }, {
+    "id" : "14",
+    "name" : "AGE_FOR_RTL_WEEK_ID",
+    "datatype" : "smallint"
+  }, {
+    "id" : "15",
+    "name" : "AGE_FOR_CS_WEEK_ID",
+    "datatype" : "smallint"
+  }, {
+    "id" : "16",
+    "name" : "DAY_OF_CAL_ID",
+    "datatype" : "integer"
+  }, {
+    "id" : "17",
+    "name" : "DAY_OF_YEAR_ID",
+    "datatype" : "smallint"
+  }, {
+    "id" : "18",
+    "name" : "DAY_OF_QTR_ID",
+    "datatype" : "smallint"
+  }, {
+    "id" : "19",
+    "name" : "DAY_OF_MONTH_ID",
+    "datatype" : "smallint"
+  }, {
+    "id" : "20",
+    "name" : "DAY_OF_WEEK_ID",
+    "datatype" : "integer"
+  }, {
+    "id" : "21",
+    "name" : "WEEK_OF_YEAR_ID",
+    "datatype" : "tinyint"
+  }, {
+    "id" : "22",
+    "name" : "WEEK_OF_CAL_ID",
+    "datatype" : "integer"
+  }, {
+    "id" : "23",
+    "name" : "MONTH_OF_QTR_ID",
+    "datatype" : "tinyint"
+  }, {
+    "id" : "24",
+    "name" : "MONTH_OF_YEAR_ID",
+    "datatype" : "tinyint"
+  }, {
+    "id" : "25",
+    "name" : "MONTH_OF_CAL_ID",
+    "datatype" : "smallint"
+  }, {
+    "id" : "26",
+    "name" : "QTR_OF_YEAR_ID",
+    "datatype" : "tinyint"
+  }, {
+    "id" : "27",
+    "name" : "QTR_OF_CAL_ID",
+    "datatype" : "smallint"
+  }, {
+    "id" : "28",
+    "name" : "YEAR_OF_CAL_ID",
+    "datatype" : "smallint"
+  }, {
+    "id" : "29",
+    "name" : "YEAR_END_DT",
+    "datatype" : "varchar(256)"
+  }, {
+    "id" : "30",
+    "name" : "QTR_END_DT",
+    "datatype" : "varchar(256)"
+  }, {
+    "id" : "31",
+    "name" : "MONTH_END_DT",
+    "datatype" : "varchar(256)"
+  }, {
+    "id" : "32",
+    "name" : "WEEK_END_DT",
+    "datatype" : "varchar(256)"
+  }, {
+    "id" : "33",
+    "name" : "CAL_DT_NAME",
+    "datatype" : "varchar(256)"
+  }, {
+    "id" : "34",
+    "name" : "CAL_DT_DESC",
+    "datatype" : "varchar(256)"
+  }, {
+    "id" : "35",
+    "name" : "CAL_DT_SHORT_NAME",
+    "datatype" : "varchar(256)"
+  }, {
+    "id" : "36",
+    "name" : "YTD_YN_ID",
+    "datatype" : "tinyint"
+  }, {
+    "id" : "37",
+    "name" : "QTD_YN_ID",
+    "datatype" : "tinyint"
+  }, {
+    "id" : "38",
+    "name" : "MTD_YN_ID",
+    "datatype" : "tinyint"
+  }, {
+    "id" : "39",
+    "name" : "WTD_YN_ID",
+    "datatype" : "tinyint"
+  }, {
+    "id" : "40",
+    "name" : "SEASON_BEG_DT",
+    "datatype" : "varchar(256)"
+  }, {
+    "id" : "41",
+    "name" : "DAY_IN_YEAR_COUNT",
+    "datatype" : "smallint"
+  }, {
+    "id" : "42",
+    "name" : "DAY_IN_QTR_COUNT",
+    "datatype" : "tinyint"
+  }, {
+    "id" : "43",
+    "name" : "DAY_IN_MONTH_COUNT",
+    "datatype" : "tinyint"
+  }, {
+    "id" : "44",
+    "name" : "DAY_IN_WEEK_COUNT",
+    "datatype" : "tinyint"
+  }, {
+    "id" : "45",
+    "name" : "RTL_YEAR_BEG_DT",
+    "datatype" : "varchar(256)"
+  }, {
+    "id" : "46",
+    "name" : "RTL_QTR_BEG_DT",
+    "datatype" : "varchar(256)"
+  }, {
+    "id" : "47",
+    "name" : "RTL_MONTH_BEG_DT",
+    "datatype" : "varchar(256)"
+  }, {
+    "id" : "48",
+    "name" : "RTL_WEEK_BEG_DT",
+    "datatype" : "varchar(256)"
+  }, {
+    "id" : "49",
+    "name" : "CS_WEEK_BEG_DT",
+    "datatype" : "varchar(256)"
+  }, {
+    "id" : "50",
+    "name" : "CAL_DATE",
+    "datatype" : "varchar(256)"
+  }, {
+    "id" : "51",
+    "name" : "DAY_OF_WEEK",
+    "datatype" : "varchar(256)"
+  }, {
+    "id" : "52",
+    "name" : "MONTH_ID",
+    "datatype" : "varchar(256)"
+  }, {
+    "id" : "53",
+    "name" : "PRD_DESC",
+    "datatype" : "varchar(256)"
+  }, {
+    "id" : "54",
+    "name" : "PRD_FLAG",
+    "datatype" : "varchar(256)"
+  }, {
+    "id" : "55",
+    "name" : "PRD_ID",
+    "datatype" : "varchar(256)"
+  }, {
+    "id" : "56",
+    "name" : "PRD_IND",
+    "datatype" : "varchar(256)"
+  }, {
+    "id" : "57",
+    "name" : "QTR_DESC",
+    "datatype" : "varchar(256)"
+  }, {
+    "id" : "58",
+    "name" : "QTR_ID",
+    "datatype" : "varchar(256)"
+  }, {
+    "id" : "59",
+    "name" : "QTR_IND",
+    "datatype" : "varchar(256)"
+  }, {
+    "id" : "60",
+    "name" : "RETAIL_WEEK",
+    "datatype" : "varchar(256)"
+  }, {
+    "id" : "61",
+    "name" : "RETAIL_YEAR",
+    "datatype" : "varchar(256)"
+  }, {
+    "id" : "62",
+    "name" : "RETAIL_START_DATE",
+    "datatype" : "varchar(256)"
+  }, {
+    "id" : "63",
+    "name" : "RETAIL_WK_END_DATE",
+    "datatype" : "varchar(256)"
+  }, {
+    "id" : "64",
+    "name" : "WEEK_IND",
+    "datatype" : "varchar(256)"
+  }, {
+    "id" : "65",
+    "name" : "WEEK_NUM_DESC",
+    "datatype" : "varchar(256)"
+  }, {
+    "id" : "66",
+    "name" : "WEEK_BEG_DATE",
+    "datatype" : "varchar(256)"
+  }, {
+    "id" : "67",
+    "name" : "WEEK_END_DATE",
+    "datatype" : "varchar(256)"
+  }, {
+    "id" : "68",
+    "name" : "WEEK_IN_YEAR_ID",
+    "datatype" : "varchar(256)"
+  }, {
+    "id" : "69",
+    "name" : "WEEK_ID",
+    "datatype" : "varchar(256)"
+  }, {
+    "id" : "70",
+    "name" : "WEEK_BEG_END_DESC_MDY",
+    "datatype" : "varchar(256)"
+  }, {
+    "id" : "71",
+    "name" : "WEEK_BEG_END_DESC_MD",
+    "datatype" : "varchar(256)"
+  }, {
+    "id" : "72",
+    "name" : "YEAR_ID",
+    "datatype" : "varchar(256)"
+  }, {
+    "id" : "73",
+    "name" : "YEAR_IND",
+    "datatype" : "varchar(256)"
+  }, {
+    "id" : "74",
+    "name" : "CAL_DT_MNS_1YEAR_DT",
+    "datatype" : "varchar(256)"
+  }, {
+    "id" : "75",
+    "name" : "CAL_DT_MNS_2YEAR_DT",
+    "datatype" : "varchar(256)"
+  }, {
+    "id" : "76",
+    "name" : "CAL_DT_MNS_1QTR_DT",
+    "datatype" : "varchar(256)"
+  }, {
+    "id" : "77",
+    "name" : "CAL_DT_MNS_2QTR_DT",
+    "datatype" : "varchar(256)"
+  }, {
+    "id" : "78",
+    "name" : "CAL_DT_MNS_1MONTH_DT",
+    "datatype" : "varchar(256)"
+  }, {
+    "id" : "79",
+    "name" : "CAL_DT_MNS_2MONTH_DT",
+    "datatype" : "varchar(256)"
+  }, {
+    "id" : "80",
+    "name" : "CAL_DT_MNS_1WEEK_DT",
+    "datatype" : "varchar(256)"
+  }, {
+    "id" : "81",
+    "name" : "CAL_DT_MNS_2WEEK_DT",
+    "datatype" : "varchar(256)"
+  }, {
+    "id" : "82",
+    "name" : "CURR_CAL_DT_MNS_1YEAR_YN_ID",
+    "datatype" : "tinyint"
+  }, {
+    "id" : "83",
+    "name" : "CURR_CAL_DT_MNS_2YEAR_YN_ID",
+    "datatype" : "tinyint"
+  }, {
+    "id" : "84",
+    "name" : "CURR_CAL_DT_MNS_1QTR_YN_ID",
+    "datatype" : "tinyint"
+  }, {
+    "id" : "85",
+    "name" : "CURR_CAL_DT_MNS_2QTR_YN_ID",
+    "datatype" : "tinyint"
+  }, {
+    "id" : "86",
+    "name" : "CURR_CAL_DT_MNS_1MONTH_YN_ID",
+    "datatype" : "tinyint"
+  }, {
+    "id" : "87",
+    "name" : "CURR_CAL_DT_MNS_2MONTH_YN_ID",
+    "datatype" : "tinyint"
+  }, {
+    "id" : "88",
+    "name" : "CURR_CAL_DT_MNS_1WEEK_YN_IND",
+    "datatype" : "tinyint"
+  }, {
+    "id" : "89",
+    "name" : "CURR_CAL_DT_MNS_2WEEK_YN_IND",
+    "datatype" : "tinyint"
+  }, {
+    "id" : "90",
+    "name" : "RTL_MONTH_OF_RTL_YEAR_ID",
+    "datatype" : "varchar(256)"
+  }, {
+    "id" : "91",
+    "name" : "RTL_QTR_OF_RTL_YEAR_ID",
+    "datatype" : "tinyint"
+  }, {
+    "id" : "92",
+    "name" : "RTL_WEEK_OF_RTL_YEAR_ID",
+    "datatype" : "tinyint"
+  }, {
+    "id" : "93",
+    "name" : "SEASON_OF_YEAR_ID",
+    "datatype" : "tinyint"
+  }, {
+    "id" : "94",
+    "name" : "YTM_YN_ID",
+    "datatype" : "tinyint"
+  }, {
+    "id" : "95",
+    "name" : "YTQ_YN_ID",
+    "datatype" : "tinyint"
+  }, {
+    "id" : "96",
+    "name" : "YTW_YN_ID",
+    "datatype" : "tinyint"
+  }, {
+    "id" : "97",
+    "name" : "CAL_DT_CRE_DATE",
+    "datatype" : "varchar(256)"
+  }, {
+    "id" : "98",
+    "name" : "CAL_DT_CRE_USER",
+    "datatype" : "varchar(256)"
+  }, {
+    "id" : "99",
+    "name" : "CAL_DT_UPD_DATE",
+    "datatype" : "varchar(256)"
+  }, {
+    "id" : "100",
+    "name" : "CAL_DT_UPD_USER",
+    "datatype" : "varchar(256)"
+  } ],
+  "database" : "edw",
+  "last_modified" : 0
+}
\ No newline at end of file
diff --git a/server-base/src/test/resources/update/table/TEST.COUNTRY.json b/server-base/src/test/resources/update/table/TEST.COUNTRY.json
new file mode 100644
index 0000000..b6c1ee0
--- /dev/null
+++ b/server-base/src/test/resources/update/table/TEST.COUNTRY.json
@@ -0,0 +1,26 @@
+{
+  "uuid" : "e286e39e-40d7-44c2-8fa2-41b365632882",
+ 
+  "name" : "COUNTRY",
+  "columns" : [ {
+    "id" : "1",
+    "name" : "COUNTRY",
+    "datatype" : "varchar(256)",
+    "index": "T"
+  }, {
+    "id" : "2",
+    "name" : "LATITUDE",
+    "datatype" : "double"
+  }, {
+    "id" : "3",
+    "name" : "LONGITUDE",
+    "datatype" : "double"
+  }, {
+    "id" : "4",
+    "name" : "NAME",
+    "datatype" : "varchar(256)",
+    "index": "T"
+  } ],
+  "database" : "TEST",
+  "last_modified" : 0
+}
\ No newline at end of file
diff --git a/server-base/src/test/resources/update/table/TEST.KYLIN_FACT.json b/server-base/src/test/resources/update/table/TEST.KYLIN_FACT.json
new file mode 100644
index 0000000..879f969
--- /dev/null
+++ b/server-base/src/test/resources/update/table/TEST.KYLIN_FACT.json
@@ -0,0 +1,73 @@
+{
+  "uuid" : "e286e39e-40d7-44c2-8fa2-41b365522771",
+  "name" : "KYLIN_FACT",
+  "data_gen" : "1",
+  "columns" : [ {
+    "id" : "1",
+    "name" : "TRANS_ID",
+    "datatype" : "bigint",
+    "data_gen" : "ID"
+  }, {
+    "id" : "2",
+    "name" : "ORDER_ID",
+    "datatype" : "bigint",
+    "index": "T"
+  }, {
+    "id" : "3",
+    "name" : "CAL_DT",
+    "datatype" : "date",
+    "data_gen" : "FK,order",
+    "index": "T"
+  }, {
+    "id" : "4",
+    "name" : "LSTG_FORMAT_NAME",
+    "datatype" : "varchar(256)",
+    "data_gen" : "FP-GTC|FP-non GTC|ABIN|Auction|Others",
+    "index": "T"
+  }, {
+    "id" : "5",
+    "name" : "LEAF_CATEG_ID",
+    "datatype" : "bigint",
+    "data_gen" : "FK,null,nullstr=0",
+    "index": "T"
+  }, {
+    "id" : "6",
+    "name" : "LSTG_SITE_ID",
+    "datatype" : "integer",
+    "index": "T"
+  }, {
+    "id" : "7",
+    "name" : "SLR_SEGMENT_CD",
+    "datatype" : "smallint",
+    "data_gen" : "FK,pk=EDW.TEST_SELLER_TYPE_DIM_TABLE.SELLER_TYPE_CD",
+    "index": "T"
+  }, {
+    "id" : "8",
+    "name" : "SELLER_ID",
+    "datatype" : "integer",
+    "data_gen" : "RAND||10000000|10001000",
+    "index": "T"
+  }, {
+    "id" : "9",
+    "name" : "PRICE",
+    "datatype" : "decimal(19,4)",
+    "data_gen" : "RAND|.##|-100|1000"
+  }, {
+    "id" : "10",
+    "name" : "ITEM_COUNT",
+    "datatype" : "integer",
+    "data_gen" : "RAND"
+  }, {
+    "id" : "11",
+    "name" : "TEST_COUNT_DISTINCT_BITMAP",
+    "datatype" : "varchar(256)",
+    "data_gen" : "RAND"
+  }, {
+    "id" : "12",
+    "name" : "TEST_COUNT_COLUMN",
+    "datatype" : "varchar(256)",
+    "data_gen" : "RAND,null=true,nullpct=0.2"
+  } ],
+  "database" : "TEST",
+  "last_modified" : 0
+}
diff --git a/server-base/src/test/resources/update/table/TEST.TEST_ACCOUNT.json b/server-base/src/test/resources/update/table/TEST.TEST_ACCOUNT.json
new file mode 100644
index 0000000..8facbea
--- /dev/null
+++ b/server-base/src/test/resources/update/table/TEST.TEST_ACCOUNT.json
@@ -0,0 +1,36 @@
+{
+  "uuid" : "f386e39e-40d7-44c2-9eb3-41b365632231",
+ 
+  "name" : "TEST_ACCOUNT",
+  "data_gen" : "2000",
+  
+  "columns" : [ {
+    "id" : "1",
+    "name" : "ACCOUNT_ID",
+    "datatype" : "bigint",
+    "data_gen" : "ID|10000000",
+    "index": "T"
+  }, {
+    "id" : "2",
+    "name" : "ACCOUNT_BUYER_LEVEL",
+    "datatype" : "integer",
+    "data_gen" : "RAND||0|5"
+  }, {
+    "id" : "3",
+    "name" : "ACCOUNT_SELLER_LEVEL",
+    "datatype" : "integer",
+    "data_gen" : "RAND||0|5"
+  }, {
+    "id" : "4",
+    "name" : "ACCOUNT_COUNTRY",
+    "datatype" : "varchar(256)",
+    "data_gen" : "CN|FR|GB|GE|JP|IT|RU|US",
+    "index": "T"
+  }, {
+    "id" : "5",
+    "name" : "ACCOUNT_CONTACT",
+    "datatype" : "varchar(256)"
+  } ],
+  "database" : "TEST",
+  "last_modified" : 0
+}
\ No newline at end of file
diff --git a/server-base/src/test/resources/update/table/TEST.TEST_CATEGORY_GROUPINGS.json b/server-base/src/test/resources/update/table/TEST.TEST_CATEGORY_GROUPINGS.json
new file mode 100644
index 0000000..bc7eebb
--- /dev/null
+++ b/server-base/src/test/resources/update/table/TEST.TEST_CATEGORY_GROUPINGS.json
@@ -0,0 +1,155 @@
+{
+ 
+  "uuid" : "952d11b5-69d9-45d1-92af-227489485e3f",
+  "name" : "TEST_CATEGORY_GROUPINGS",
+  "columns" : [ {
+    "id" : "1",
+    "name" : "LEAF_CATEG_ID",
+    "datatype" : "bigint",
+    "index": "T"
+  }, {
+    "id" : "2",
+    "name" : "LEAF_CATEG_NAME",
+    "datatype" : "string",
+    "index": "T"
+  }, {
+    "id" : "3",
+    "name" : "SITE_ID",
+    "datatype" : "int",
+    "index": "T"
+  }, {
+    "id" : "4",
+    "name" : "CATEG_BUSN_MGR",
+    "datatype" : "string"
+  }, {
+    "id" : "5",
+    "name" : "CATEG_BUSN_UNIT",
+    "datatype" : "string"
+  }, {
+    "id" : "6",
+    "name" : "REGN_CATEG",
+    "datatype" : "string"
+  }, {
+    "id" : "7",
+    "name" : "USER_DEFINED_FIELD1",
+    "datatype" : "string"
+  }, {
+    "id" : "8",
+    "name" : "USER_DEFINED_FIELD3",
+    "datatype" : "string"
+  }, {
+    "id" : "9",
+    "name" : "GROUPINGS_CRE_DATE",
+    "datatype" : "string"
+  }, {
+    "id" : "10",
+    "name" : "UPD_DATE",
+    "datatype" : "string"
+  }, {
+    "id" : "11",
+    "name" : "GROUPINGS_CRE_USER",
+    "datatype" : "string"
+  }, {
+    "id" : "12",
+    "name" : "UPD_USER",
+    "datatype" : "string"
+  }, {
+    "id" : "13",
+    "name" : "META_CATEG_ID",
+    "datatype" : "decimal"
+  }, {
+    "id" : "14",
+    "name" : "META_CATEG_NAME",
+    "datatype" : "string"
+  }, {
+    "id" : "15",
+    "name" : "CATEG_LVL2_ID",
+    "datatype" : "decimal"
+  }, {
+    "id" : "16",
+    "name" : "CATEG_LVL3_ID",
+    "datatype" : "decimal"
+  }, {
+    "id" : "17",
+    "name" : "CATEG_LVL4_ID",
+    "datatype" : "decimal"
+  }, {
+    "id" : "18",
+    "name" : "CATEG_LVL5_ID",
+    "datatype" : "decimal"
+  }, {
+    "id" : "19",
+    "name" : "CATEG_LVL6_ID",
+    "datatype" : "decimal"
+  }, {
+    "id" : "20",
+    "name" : "CATEG_LVL7_ID",
+    "datatype" : "decimal"
+  }, {
+    "id" : "21",
+    "name" : "CATEG_LVL2_NAME",
+    "datatype" : "string"
+  }, {
+    "id" : "22",
+    "name" : "CATEG_LVL3_NAME",
+    "datatype" : "string"
+  }, {
+    "id" : "23",
+    "name" : "CATEG_LVL4_NAME",
+    "datatype" : "string"
+  }, {
+    "id" : "24",
+    "name" : "CATEG_LVL5_NAME",
+    "datatype" : "string"
+  }, {
+    "id" : "25",
+    "name" : "CATEG_LVL6_NAME",
+    "datatype" : "string"
+  }, {
+    "id" : "26",
+    "name" : "CATEG_LVL7_NAME",
+    "datatype" : "string"
+  }, {
+    "id" : "27",
+    "name" : "CATEG_FLAGS",
+    "datatype" : "decimal"
+  }, {
+    "id" : "28",
+    "name" : "ADULT_CATEG_YN",
+    "datatype" : "string"
+  }, {
+    "id" : "29",
+    "name" : "DOMAIN_ID",
+    "datatype" : "decimal"
+  }, {
+    "id" : "30",
+    "name" : "USER_DEFINED_FIELD5",
+    "datatype" : "string"
+  }, {
+    "id" : "31",
+    "name" : "VCS_ID",
+    "datatype" : "decimal"
+  }, {
+    "id" : "32",
+    "name" : "GCS_ID",
+    "datatype" : "decimal"
+  }, {
+    "id" : "33",
+    "name" : "MOVE_TO",
+    "datatype" : "decimal"
+  }, {
+    "id" : "34",
+    "name" : "SAP_CATEGORY_ID",
+    "datatype" : "decimal"
+  }, {
+    "id" : "35",
+    "name" : "SRC_ID",
+    "datatype" : "tinyint"
+  }, {
+    "id" : "36",
+    "name" : "BSNS_VRTCL_NAME",
+    "datatype" : "string"
+  } ],
+  "database" : "TEST",
+  "last_modified" : 0
+}
\ No newline at end of file