danny0405 commented on a change in pull request #1374: [CALCITE-3138] 
RelStructuredTypeFlattener doesn't restructure ROW type fields
URL: https://github.com/apache/calcite/pull/1374#discussion_r315646237
 
 

 ##########
 File path: 
core/src/main/java/org/apache/calcite/sql2rel/RelStructuredTypeFlattener.java
 ##########
 @@ -587,86 +561,103 @@ private void flattenProjections(RewriteRexShuttle 
shuttle,
       List<Pair<RexNode, String>> flattenedExps) {
     for (int i = 0; i < exps.size(); ++i) {
       RexNode exp = exps.get(i);
-      String fieldName =
-          (fieldNames == null || fieldNames.get(i) == null)
-              ? ("$" + i)
-              : fieldNames.get(i);
-      if (!prefix.equals("")) {
-        fieldName = prefix + "$" + fieldName;
-      }
+      String fieldName = extractName(fieldNames, prefix, i);
       flattenProjection(shuttle, exp, fieldName, flattenedExps);
     }
   }
 
+  private String extractName(List<String> fieldNames, String prefix, int i) {
+    String fieldName = (fieldNames == null || fieldNames.get(i) == null)
+        ? ("$" + i)
+        : fieldNames.get(i);
+    if (!prefix.equals("")) {
+      fieldName = prefix + "$" + fieldName;
+    }
+    return fieldName;
+  }
+
   private void flattenProjection(RewriteRexShuttle shuttle,
       RexNode exp,
       String fieldName,
       List<Pair<RexNode, String>> flattenedExps) {
     if (exp.getType().isStruct()) {
       if (exp instanceof RexInputRef) {
-        RexInputRef inputRef = (RexInputRef) exp;
-
-        // expand to range
-        RelDataType flattenedType =
-            SqlTypeUtil.flattenRecordType(
-                rexBuilder.getTypeFactory(),
-                exp.getType(),
-                null);
-        List<RelDataTypeField> fieldList = flattenedType.getFieldList();
-        int n = fieldList.size();
-        for (int j = 0; j < n; ++j) {
-          final Ord<RelDataType> newField =
-              getNewFieldForOldInput(inputRef.getIndex(), j);
-          flattenedExps.add(
-              Pair.of(new RexInputRef(newField.i, newField.e),
-                  fieldName));
+        final int oldOrdinal = ((RexInputRef) exp).getIndex();
+        final int flattenFieldsCount = postFlattenSize(exp.getType());
+        for (int innerOrdinal = 0; innerOrdinal < flattenFieldsCount; 
innerOrdinal++) {
+          Ord<RelDataType> newField = getNewFieldForOldInput(oldOrdinal, 
innerOrdinal);
+          RexInputRef newRef = new RexInputRef(newField.i, newField.e);
+          flattenedExps.add(Pair.of(newRef, fieldName));
         }
       } else if (isConstructor(exp) || exp.isA(SqlKind.CAST)) {
         // REVIEW jvs 27-Feb-2005:  for cast, see corresponding note
         // in RewriteRexShuttle
         RexCall call = (RexCall) exp;
-        if (exp.isA(SqlKind.NEW_SPECIFICATION)) {
-          // For object constructors, prepend a FALSE null
-          // indicator.
-          flattenedExps.add(
-              Pair.of(rexBuilder.makeLiteral(false),
-                  fieldName));
-        } else if (exp.isA(SqlKind.CAST)) {
-          if (RexLiteral.isNullLiteral(
-              ((RexCall) exp).operands.get(0))) {
-            // Translate CAST(NULL AS UDT) into
-            // the correct number of null fields.
-            flattenNullLiteral(
-                exp.getType(),
-                flattenedExps);
-            return;
-          }
+        if (exp.isA(SqlKind.CAST)
+            && RexLiteral.isNullLiteral(call.operands.get(0))) {
+          // Translate CAST(NULL AS UDT) into
+          // the correct number of null fields.
+          flattenNullLiteral(exp.getType(), flattenedExps);
+          return;
         }
-        flattenProjections(new RewriteRexShuttle(),
+        flattenProjections(shuttle,
             call.getOperands(),
             Collections.nCopies(call.getOperands().size(), null),
             fieldName,
             flattenedExps);
       } else if (exp instanceof RexCall) {
         // NOTE jvs 10-Feb-2005:  This is a lame hack to keep special
         // functions which return row types working.
-
-        int j = 0;
         RexNode newExp = exp;
-        List<RexNode> oldOperands = ((RexCall) exp).getOperands();
-        if (oldOperands.get(0) instanceof RexInputRef) {
-          final RexInputRef inputRef = (RexInputRef) oldOperands.get(0);
-          final Ord<RelDataType> newField =
-              getNewFieldForOldInput(inputRef.getIndex());
-          newExp = rexBuilder.makeCall(exp.getType(),
-              ((RexCall) exp).getOperator(),
-              ImmutableList.of(rexBuilder.makeInputRef(newField.e, newField.i),
-                  oldOperands.get(1)));
-        }
-        for (RelDataTypeField field : newExp.getType().getFieldList()) {
-          flattenedExps.add(
-              Pair.of(rexBuilder.makeFieldAccess(newExp, field.getIndex()),
-                  fieldName + "$" + (j++)));
+        List<RexNode> operands = ((RexCall) exp).getOperands();
+        SqlOperator operator = ((RexCall) exp).getOperator();
+
+        if (operator == SqlStdOperatorTable.ITEM
+            && operands.get(0).getType().isStruct()
+            && operands.get(1).isA(SqlKind.LITERAL)
+            && SqlTypeUtil.inCharFamily(operands.get(1).getType())) {
+          String literalString = ((RexLiteral) 
operands.get(1)).getValueAs(String.class);
+          RexNode firstOp = operands.get(0);
+
+          if (firstOp instanceof RexInputRef) {
+            // when performed getting field from struct exp by field name
+            // and new input is flattened it's enough to refer target field by 
index.
+            // But it's possible that requested field is also of type struct, 
that's
+            // why we're trying to get range from to. For primitive just one 
field will be in range.
+            int from = 0;
+            for (RelDataTypeField field : firstOp.getType().getFieldList()) {
+              if (literalString.equalsIgnoreCase(field.getName())) {
+                int oldOrdinal = ((RexInputRef) firstOp).getIndex();
+                int to = from + postFlattenSize(field.getType());
+                for (int newInnerOrdinal = from; newInnerOrdinal < to; 
newInnerOrdinal++) {
+                  Ord<RelDataType> newField = 
getNewFieldForOldInput(oldOrdinal, newInnerOrdinal);
+                  RexInputRef newRef = rexBuilder.makeInputRef(newField.e, 
newField.i);
+                  flattenedExps.add(Pair.of(newRef, fieldName));
+                }
+                break;
+              } else {
+                from += postFlattenSize(field.getType());
+              }
+            }
+          } else if (firstOp instanceof RexCall) {
+            // to get nested struct from return type of firstOp rex call,
+            // we need to flatten firstOp and get range of expressions which
+            // corresponding to desirable nested struct flattened fields
+            List<Pair<RexNode, String>> firstOpFlattenedExps = new 
ArrayList<>();
+            flattenProjection(shuttle, firstOp, fieldName, 
firstOpFlattenedExps);
+            int newInnerOrdinal = getNewInnerOrdinal(firstOp, literalString);
 
 Review comment:
   Maybe we should use a name pattern like `fieldName$0`, if it was in a deep 
recursive, the name may be like `fieldName$0$0...`

----------------------------------------------------------------
This is an automated message from the Apache Git Service.
To respond to the message, please log on to GitHub and use the
URL above to go to the specific comment.
 
For queries about this service, please contact Infrastructure at:
[email protected]


With regards,
Apache Git Services

Reply via email to