You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@lens.apache.org by pr...@apache.org on 2015/04/13 15:24:09 UTC

incubator-lens git commit: LENS-455: Allow partition columns to be queried as field in where/select clause.

Repository: incubator-lens
Updated Branches:
  refs/heads/master 2391a8080 -> 7586a83c8


LENS-455: Allow partition columns to be queried as field in where/select clause.


Project: http://git-wip-us.apache.org/repos/asf/incubator-lens/repo
Commit: http://git-wip-us.apache.org/repos/asf/incubator-lens/commit/7586a83c
Tree: http://git-wip-us.apache.org/repos/asf/incubator-lens/tree/7586a83c
Diff: http://git-wip-us.apache.org/repos/asf/incubator-lens/diff/7586a83c

Branch: refs/heads/master
Commit: 7586a83c8aead80c0b8e21300623ea50eb61ac34
Parents: 2391a80
Author: Rajat Khandelwal <ra...@gmail.com>
Authored: Mon Apr 13 18:53:57 2015 +0530
Committer: Rajat Khandelwal <ra...@gmail.com>
Committed: Mon Apr 13 18:53:57 2015 +0530

----------------------------------------------------------------------
 lens-api/src/main/resources/cube-0.1.xsd        |  6 ++-
 .../lens/cube/metadata/CubeDimensionTable.java  | 34 ++++++++++----
 .../lens/cube/metadata/MetastoreConstants.java  |  1 +
 .../lens/cube/metadata/MetastoreUtil.java       |  4 ++
 .../apache/lens/cube/parse/CandidateDim.java    |  3 +-
 .../cube/parse/CandidateTablePruneCause.java    |  2 +-
 .../cube/metadata/TestCubeMetastoreClient.java  |  8 ----
 .../apache/lens/cube/parse/CubeTestSetup.java   | 30 +++++++++++++
 .../lens/cube/parse/TestCubeRewriter.java       | 47 ++++++++++++++++++--
 .../src/main/resources/product_table.xml        |  3 +-
 10 files changed, 113 insertions(+), 25 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/incubator-lens/blob/7586a83c/lens-api/src/main/resources/cube-0.1.xsd
----------------------------------------------------------------------
diff --git a/lens-api/src/main/resources/cube-0.1.xsd b/lens-api/src/main/resources/cube-0.1.xsd
index 1de4258..24d0d64 100644
--- a/lens-api/src/main/resources/cube-0.1.xsd
+++ b/lens-api/src/main/resources/cube-0.1.xsd
@@ -528,7 +528,11 @@
       <xs:element type="x_properties" name="properties" maxOccurs="1" minOccurs="0">
         <xs:annotation>
           <xs:documentation>
-            Dimension table properties
+            Dimension table properties. The properties that should be set are:
+            1.  dimtable.{dim_table_name}.part.cols = comma separated list of partition columns of this dimtable.
+                This would basically be union of all partition columns of all storage tables of the dimtable.
+                Setting this makes that partition column queryable.
+                Time part columns can be skipped as they will generally not be queried.
           </xs:documentation>
         </xs:annotation>
       </xs:element>

http://git-wip-us.apache.org/repos/asf/incubator-lens/blob/7586a83c/lens-cube/src/main/java/org/apache/lens/cube/metadata/CubeDimensionTable.java
----------------------------------------------------------------------
diff --git a/lens-cube/src/main/java/org/apache/lens/cube/metadata/CubeDimensionTable.java b/lens-cube/src/main/java/org/apache/lens/cube/metadata/CubeDimensionTable.java
index 33e0482..47a30dd 100644
--- a/lens-cube/src/main/java/org/apache/lens/cube/metadata/CubeDimensionTable.java
+++ b/lens-cube/src/main/java/org/apache/lens/cube/metadata/CubeDimensionTable.java
@@ -25,6 +25,8 @@ import org.apache.hadoop.hive.metastore.api.FieldSchema;
 import org.apache.hadoop.hive.ql.metadata.HiveException;
 import org.apache.hadoop.hive.ql.metadata.Table;
 
+import com.google.common.collect.Sets;
+
 public final class CubeDimensionTable extends AbstractCubeTable {
   private String dimName; // dimension name the dimtabe belongs to
   private final Map<String, UpdatePeriod> snapshotDumpPeriods = new HashMap<String, UpdatePeriod>();
@@ -54,6 +56,7 @@ public final class CubeDimensionTable extends AbstractCubeTable {
     addProperties();
   }
 
+
   private static Map<String, UpdatePeriod> getSnapshotDumpPeriods(Set<String> storages) {
     Map<String, UpdatePeriod> snapshotDumpPeriods = new HashMap<String, UpdatePeriod>();
     for (String storage : storages) {
@@ -71,6 +74,19 @@ public final class CubeDimensionTable extends AbstractCubeTable {
     }
   }
 
+  public Set<String> getPartCols() {
+    Set<String> partCols = Sets.newHashSet();
+    String partColsStr = getProperties().get(MetastoreUtil.getDimTablePartsKey(getName()));
+    if (partColsStr != null) {
+      for (String s : StringUtils.split(partColsStr, ",")) {
+        if (!StringUtils.isBlank(s)) {
+          partCols.add(s);
+        }
+      }
+    }
+    return partCols;
+  }
+
   @Override
   public CubeTableType getTableType() {
     return CubeTableType.DIM_TABLE;
@@ -79,10 +95,11 @@ public final class CubeDimensionTable extends AbstractCubeTable {
   @Override
   protected void addProperties() {
     super.addProperties();
-    setDimName(getProperties(), getName(), dimName);
-    setSnapshotPeriods(getName(), getProperties(), snapshotDumpPeriods);
+    setDimName(getName(), getProperties(), dimName);
+    setSnapshotDumpPeriods(getName(), getProperties(), snapshotDumpPeriods);
   }
 
+
   public Map<String, UpdatePeriod> getSnapshotDumpPeriods() {
     return snapshotDumpPeriods;
   }
@@ -91,7 +108,7 @@ public final class CubeDimensionTable extends AbstractCubeTable {
     return dimName;
   }
 
-  private static void setSnapshotPeriods(String name, Map<String, String> props,
+  private static void setSnapshotDumpPeriods(String name, Map<String, String> props,
     Map<String, UpdatePeriod> snapshotDumpPeriods) {
     if (snapshotDumpPeriods != null) {
       props.put(MetastoreUtil.getDimensionStorageListKey(name), MetastoreUtil.getStr(snapshotDumpPeriods.keySet()));
@@ -103,7 +120,7 @@ public final class CubeDimensionTable extends AbstractCubeTable {
     }
   }
 
-  private static void setDimName(Map<String, String> props, String dimTblName, String dimName) {
+  private static void setDimName(String dimTblName, Map<String, String> props, String dimName) {
     props.put(MetastoreUtil.getDimNameKey(dimTblName), dimName);
   }
 
@@ -115,8 +132,7 @@ public final class CubeDimensionTable extends AbstractCubeTable {
     String storagesStr = params.get(MetastoreUtil.getDimensionStorageListKey(name));
     if (!StringUtils.isBlank(storagesStr)) {
       Map<String, UpdatePeriod> dumpPeriods = new HashMap<String, UpdatePeriod>();
-      String[] storages = storagesStr.split(",");
-      for (String storage : storages) {
+      for (String storage : StringUtils.split(storagesStr, ",")) {
         String dumpPeriod = params.get(MetastoreUtil.getDimensionDumpPeriodKey(name, storage));
         if (dumpPeriod != null) {
           dumpPeriods.put(storage, UpdatePeriod.valueOf(dumpPeriod));
@@ -178,7 +194,7 @@ public final class CubeDimensionTable extends AbstractCubeTable {
    */
   public void alterUberDim(String newDimName) {
     this.dimName = newDimName;
-    setDimName(getProperties(), getName(), this.dimName);
+    setDimName(getName(), getProperties(), this.dimName);
   }
 
   /**
@@ -198,7 +214,7 @@ public final class CubeDimensionTable extends AbstractCubeTable {
     }
 
     snapshotDumpPeriods.put(storage, period);
-    setSnapshotPeriods(getName(), getProperties(), snapshotDumpPeriods);
+    setSnapshotDumpPeriods(getName(), getProperties(), snapshotDumpPeriods);
   }
 
   @Override
@@ -213,6 +229,6 @@ public final class CubeDimensionTable extends AbstractCubeTable {
 
   void dropStorage(String storage) {
     snapshotDumpPeriods.remove(storage);
-    setSnapshotPeriods(getName(), getProperties(), snapshotDumpPeriods);
+    setSnapshotDumpPeriods(getName(), getProperties(), snapshotDumpPeriods);
   }
 }

http://git-wip-us.apache.org/repos/asf/incubator-lens/blob/7586a83c/lens-cube/src/main/java/org/apache/lens/cube/metadata/MetastoreConstants.java
----------------------------------------------------------------------
diff --git a/lens-cube/src/main/java/org/apache/lens/cube/metadata/MetastoreConstants.java b/lens-cube/src/main/java/org/apache/lens/cube/metadata/MetastoreConstants.java
index e25fc81..e7f10ac 100644
--- a/lens-cube/src/main/java/org/apache/lens/cube/metadata/MetastoreConstants.java
+++ b/lens-cube/src/main/java/org/apache/lens/cube/metadata/MetastoreConstants.java
@@ -42,6 +42,7 @@ public final class MetastoreConstants {
   // Uber dimension constants
   public static final String DIMENSION_PFX = "dimension.";
   public static final String ATTRIBUTES_LIST_SFX = ".attributes.list";
+  public static final String PARTCOLS_SFX = ".part.cols";
   public static final String TIMED_DIMENSION_SFX = ".timed.dimension";
 
   // fact constants

http://git-wip-us.apache.org/repos/asf/incubator-lens/blob/7586a83c/lens-cube/src/main/java/org/apache/lens/cube/metadata/MetastoreUtil.java
----------------------------------------------------------------------
diff --git a/lens-cube/src/main/java/org/apache/lens/cube/metadata/MetastoreUtil.java b/lens-cube/src/main/java/org/apache/lens/cube/metadata/MetastoreUtil.java
index 30253d3..bdec4e3 100644
--- a/lens-cube/src/main/java/org/apache/lens/cube/metadata/MetastoreUtil.java
+++ b/lens-cube/src/main/java/org/apache/lens/cube/metadata/MetastoreUtil.java
@@ -62,6 +62,10 @@ public class MetastoreUtil {
     return getDimPrefix(dimName) + ATTRIBUTES_LIST_SFX;
   }
 
+  public static final String getDimTablePartsKey(String dimtableName) {
+    return getDimensionTablePrefix(dimtableName) + PARTCOLS_SFX;
+  }
+
   public static final String getDimTimedDimensionKey(String dimName) {
     return getDimPrefix(dimName) + TIMED_DIMENSION_SFX;
   }

http://git-wip-us.apache.org/repos/asf/incubator-lens/blob/7586a83c/lens-cube/src/main/java/org/apache/lens/cube/parse/CandidateDim.java
----------------------------------------------------------------------
diff --git a/lens-cube/src/main/java/org/apache/lens/cube/parse/CandidateDim.java b/lens-cube/src/main/java/org/apache/lens/cube/parse/CandidateDim.java
index 90d0b6d..64dff16 100644
--- a/lens-cube/src/main/java/org/apache/lens/cube/parse/CandidateDim.java
+++ b/lens-cube/src/main/java/org/apache/lens/cube/parse/CandidateDim.java
@@ -29,6 +29,7 @@ import org.apache.lens.cube.metadata.StorageConstants;
 import org.apache.commons.lang.StringUtils;
 import org.apache.hadoop.hive.ql.session.SessionState;
 
+import com.google.common.collect.Sets;
 import lombok.Getter;
 import lombok.Setter;
 
@@ -117,7 +118,7 @@ public class CandidateDim implements CandidateTable {
 
   @Override
   public Collection<String> getColumns() {
-    return dimtable.getAllFieldNames();
+    return Sets.union(dimtable.getAllFieldNames(), dimtable.getPartCols());
   }
 
   @Override

http://git-wip-us.apache.org/repos/asf/incubator-lens/blob/7586a83c/lens-cube/src/main/java/org/apache/lens/cube/parse/CandidateTablePruneCause.java
----------------------------------------------------------------------
diff --git a/lens-cube/src/main/java/org/apache/lens/cube/parse/CandidateTablePruneCause.java b/lens-cube/src/main/java/org/apache/lens/cube/parse/CandidateTablePruneCause.java
index bc9ef93..2c191fc 100644
--- a/lens-cube/src/main/java/org/apache/lens/cube/parse/CandidateTablePruneCause.java
+++ b/lens-cube/src/main/java/org/apache/lens/cube/parse/CandidateTablePruneCause.java
@@ -240,7 +240,7 @@ public class CandidateTablePruneCause {
     for (Map.Entry<String, SkipStorageCause> entry : storageCauses.entrySet()) {
       String key = entry.getKey();
       key = key.substring(0, (key.indexOf("_") + key.length() + 1) % (key.length() + 1)); // extract the storage part
-      cause.getStorageCauses().put(key, entry.getValue());
+      cause.getStorageCauses().put(key.toLowerCase(), entry.getValue());
     }
     return cause;
   }

http://git-wip-us.apache.org/repos/asf/incubator-lens/blob/7586a83c/lens-cube/src/test/java/org/apache/lens/cube/metadata/TestCubeMetastoreClient.java
----------------------------------------------------------------------
diff --git a/lens-cube/src/test/java/org/apache/lens/cube/metadata/TestCubeMetastoreClient.java b/lens-cube/src/test/java/org/apache/lens/cube/metadata/TestCubeMetastoreClient.java
index fe29d25..9ceea48 100644
--- a/lens-cube/src/test/java/org/apache/lens/cube/metadata/TestCubeMetastoreClient.java
+++ b/lens-cube/src/test/java/org/apache/lens/cube/metadata/TestCubeMetastoreClient.java
@@ -1490,10 +1490,6 @@ public class TestCubeMetastoreClient {
     String storageTableName = MetastoreUtil.getFactStorageTableName(cubeFact.getName(), c1);
     List<Partition> parts = client.getPartitionsByFilter(storageTableName, "dt='latest'");
     Assert.assertEquals(parts.size(), 0);
-//    Assert
-//      .assertEquals(TextInputFormat.class.getCanonicalName(), parts.get(0).getInputFormatClass().getCanonicalName());
-//    Assert.assertEquals(parts.get(0).getParameters().get(MetastoreUtil.getLatestPartTimestampKey("dt")),
-//      UpdatePeriod.HOURLY.format().format(now));
     Assert.assertEquals(client.getAllParts(storageTableName).size(), 1);
 
     client.dropPartition(cubeFact.getName(), c1, timeParts, null, UpdatePeriod.HOURLY);
@@ -1577,10 +1573,6 @@ public class TestCubeMetastoreClient {
     Assert.assertEquals(client.getAllParts(storageTableName).size(), 1);
     List<Partition> parts = client.getPartitionsByFilter(storageTableName, "dt='latest'");
     Assert.assertEquals(parts.size(), 0);
-//    Assert
-//      .assertEquals(TextInputFormat.class.getCanonicalName(), parts.get(0).getInputFormatClass().getCanonicalName());
-//    Assert.assertEquals(parts.get(0).getParameters().get(MetastoreUtil.getLatestPartTimestampKey("dt")),
-//      UpdatePeriod.HOURLY.format().format(now));
 
     client.dropPartition(cubeFactWithParts.getName(), c1, timeParts, partSpec, UpdatePeriod.HOURLY);
     Assert.assertFalse(client.factPartitionExists(cubeFactWithParts.getName(), c1, UpdatePeriod.HOURLY, timeParts,

http://git-wip-us.apache.org/repos/asf/incubator-lens/blob/7586a83c/lens-cube/src/test/java/org/apache/lens/cube/parse/CubeTestSetup.java
----------------------------------------------------------------------
diff --git a/lens-cube/src/test/java/org/apache/lens/cube/parse/CubeTestSetup.java b/lens-cube/src/test/java/org/apache/lens/cube/parse/CubeTestSetup.java
index 62b2c95..517fe7b 100644
--- a/lens-cube/src/test/java/org/apache/lens/cube/parse/CubeTestSetup.java
+++ b/lens-cube/src/test/java/org/apache/lens/cube/parse/CubeTestSetup.java
@@ -1776,6 +1776,20 @@ public class CubeTestSetup {
     storageTables.put(c1, s1);
 
     client.createCubeDimensionTable(dimName, dimTblName, dimColumns, 0L, dumpPeriods, dimProps, storageTables);
+    dimTblName = "countrytable_partitioned";
+
+    StorageTableDesc s2 = new StorageTableDesc();
+    s2.setInputFormat(TextInputFormat.class.getCanonicalName());
+    s2.setOutputFormat(HiveIgnoreKeyTextOutputFormat.class.getCanonicalName());
+    ArrayList<FieldSchema> partCols = Lists.newArrayList();
+    partCols.add(dimColumns.remove(dimColumns.size() - 2));
+    s2.setPartCols(partCols);
+    dumpPeriods.clear();
+    dumpPeriods.put(c3, UpdatePeriod.HOURLY);
+    storageTables.clear();
+    storageTables.put(c3, s2);
+    dimProps.put(MetastoreUtil.getDimTablePartsKey(dimTblName), partCols.get(0).getName());
+    client.createCubeDimensionTable(dimName, dimTblName, dimColumns, 0L, dumpPeriods, dimProps, storageTables);
   }
 
   private void createStateTable(CubeMetastoreClient client) throws Exception {
@@ -1814,6 +1828,22 @@ public class CubeTestSetup {
     storageTables.put(c1, s1);
 
     client.createCubeDimensionTable(dimName, dimTblName, dimColumns, 0L, dumpPeriods, dimProps, storageTables);
+
+    // In this, country id will be a partition
+    dimTblName = "statetable_partitioned";
+
+    StorageTableDesc s2 = new StorageTableDesc();
+    s2.setInputFormat(TextInputFormat.class.getCanonicalName());
+    s2.setOutputFormat(HiveIgnoreKeyTextOutputFormat.class.getCanonicalName());
+    partCols.add(dimColumns.remove(dimColumns.size() - 1));
+    s2.setPartCols(partCols);
+    s2.setTimePartCols(timePartCols);
+    dumpPeriods.clear();
+    dumpPeriods.put(c3, UpdatePeriod.HOURLY);
+    storageTables.clear();
+    storageTables.put(c3, s2);
+    dimProps.put(MetastoreUtil.getDimTablePartsKey(dimTblName), partCols.get(1).getName());
+    client.createCubeDimensionTable(dimName, dimTblName, dimColumns, 0L, dumpPeriods, dimProps, storageTables);
   }
 
   public void createSources(HiveConf conf, String dbName) throws Exception {

http://git-wip-us.apache.org/repos/asf/incubator-lens/blob/7586a83c/lens-cube/src/test/java/org/apache/lens/cube/parse/TestCubeRewriter.java
----------------------------------------------------------------------
diff --git a/lens-cube/src/test/java/org/apache/lens/cube/parse/TestCubeRewriter.java b/lens-cube/src/test/java/org/apache/lens/cube/parse/TestCubeRewriter.java
index 6c65953..b85f60e 100644
--- a/lens-cube/src/test/java/org/apache/lens/cube/parse/TestCubeRewriter.java
+++ b/lens-cube/src/test/java/org/apache/lens/cube/parse/TestCubeRewriter.java
@@ -408,6 +408,38 @@ public class TestCubeRewriter extends TestQueryRewrite {
   }
 
   @Test
+  public void testPartColAsQueryColumn() throws Exception {
+    Configuration conf = getConf();
+    conf.setBoolean(CubeQueryConfUtil.FAIL_QUERY_ON_PARTIAL_DATA, false);
+    conf.set(CubeQueryConfUtil.DRIVER_SUPPORTED_STORAGES, "C3");
+    conf.setBoolean(CubeQueryConfUtil.DISABLE_AUTO_JOINS, false);
+    String hql, expected;
+    hql = rewrite(
+      "select countrydim.name, msr2 from" + " testCube" + " where countrydim.region = 'asia' and "
+        + TWO_DAYS_RANGE, conf);
+    expected =
+      getExpectedQuery(cubeName, "select countrydim.name, sum(testcube.msr2)" + " FROM ", " JOIN " + getDbName()
+          + "c3_statetable_partitioned statedim ON" + " testCube.stateid = statedim.id and statedim.dt = 'latest' JOIN "
+          + getDbName()
+          + "c3_countrytable_partitioned countrydim on statedim.countryid=countrydim.id and countrydim.dt='latest'",
+        "countrydim.region='asia'",
+        " group by countrydim.name ", null,
+        getWhereForDailyAndHourly2days(cubeName, "C3_testfact"));
+    compareQueries(hql, expected);
+    hql = rewrite(
+      "select statedim.name, statedim.countryid, msr2 from" + " testCube" + " where statedim.countryid = 5 and "
+        + TWO_DAYS_RANGE, conf);
+    expected =
+      getExpectedQuery(cubeName, "select statedim.name, statedim.countryid, sum(testcube.msr2)" + " FROM ",
+        " JOIN " + getDbName()
+          + "c3_statetable_partitioned statedim ON" + " testCube.stateid = statedim.id and statedim.dt = 'latest'",
+        "statedim.countryid=5",
+        " group by statedim.name, statedim.countryid", null,
+        getWhereForDailyAndHourly2days(cubeName, "C3_testfact"));
+    compareQueries(hql, expected);
+  }
+
+  @Test
   public void testCubeJoinQuery() throws Exception {
     // q1
     String hqlQuery =
@@ -833,19 +865,19 @@ public class TestCubeRewriter extends TestQueryRewrite {
 
     Assert.assertEquals(
       pruneCauses.getBrief().substring(0, CandidateTablePruneCode.MISSING_PARTITIONS.errorFormat.length() - 3),
-        CandidateTablePruneCode.MISSING_PARTITIONS.errorFormat.substring(0,
-          CandidateTablePruneCode.MISSING_PARTITIONS.errorFormat.length() - 3));
+      CandidateTablePruneCode.MISSING_PARTITIONS.errorFormat.substring(0,
+        CandidateTablePruneCode.MISSING_PARTITIONS.errorFormat.length() - 3));
 
     Assert.assertEquals(pruneCauses.getDetails().get("testfact").iterator().next().getCause(),
       CandidateTablePruneCode.MISSING_PARTITIONS);
     Assert.assertEquals(pruneCauses.getDetails().get("testfactmonthly").iterator().next().getCause(),
       CandidateTablePruneCode.NO_FACT_UPDATE_PERIODS_FOR_GIVEN_RANGE);
     Assert.assertEquals(pruneCauses.getDetails().get("testfact2").iterator().next().getCause(),
-        CandidateTablePruneCode.MISSING_PARTITIONS);
+      CandidateTablePruneCode.MISSING_PARTITIONS);
     Assert.assertEquals(pruneCauses.getDetails().get("testfact2_raw").iterator().next().getCause(),
       CandidateTablePruneCode.MISSING_PARTITIONS);
     Assert.assertEquals(pruneCauses.getDetails().get("cheapfact").iterator().next().getCause(),
-        CandidateTablePruneCode.NO_CANDIDATE_STORAGES);
+      CandidateTablePruneCode.NO_CANDIDATE_STORAGES);
     Assert.assertEquals(pruneCauses.getDetails().get("summary1,summary2,summary3").iterator().next().getCause(),
       CandidateTablePruneCode.MISSING_PARTITIONS);
     Assert.assertEquals(pruneCauses.getDetails().get("summary4").iterator().next()
@@ -896,6 +928,13 @@ public class TestCubeRewriter extends TestQueryRewrite {
                 }
               }))
           );
+          put("statetable_partitioned", Arrays.asList(CandidateTablePruneCause.noCandidateStorages(
+              new HashMap<String, SkipStorageCause>() {
+                {
+                  put("C3_statetable_partitioned", new SkipStorageCause(SkipStorageCode.UNSUPPORTED));
+                }
+              }))
+          );
         }
       }
     ));

http://git-wip-us.apache.org/repos/asf/incubator-lens/blob/7586a83c/lens-examples/src/main/resources/product_table.xml
----------------------------------------------------------------------
diff --git a/lens-examples/src/main/resources/product_table.xml b/lens-examples/src/main/resources/product_table.xml
index 4babca6..f7e804f 100644
--- a/lens-examples/src/main/resources/product_table.xml
+++ b/lens-examples/src/main/resources/product_table.xml
@@ -26,12 +26,12 @@
     <column comment="SKU_number" name="SKU_number" type="INT"/>
     <column comment="" name="description" type="STRING"/>
     <column comment="" name="color" type="STRING"/>
-    <column comment="" name="category" type="STRING"/>
     <column comment="" name="weight" type="FLOAT"/>
     <column comment="" name="manufacturer" type="STRING"/>
   </columns>
   <properties>
     <property name="dim4.prop" value="d1"/>
+    <property name="dimtable.product_table.part.cols" value="category"/>
   </properties>
   <storage_tables>
     <storage_table>
@@ -42,6 +42,7 @@
       <table_desc external="true" field_delimiter="," table_location="/tmp/examples/product">
         <part_cols>
           <column comment="Time column" name="dt" type="STRING"/>
+          <column comment="Category" name="category" type="STRING"/>
         </part_cols>
         <time_part_cols>dt</time_part_cols>
       </table_desc>