Esempio n. 1
0
  /**
   * Returns the type the row which results when two relations are joined.
   *
   * <p>The resulting row type consists of the system fields (if any), followed by the fields of the
   * left type, followed by the fields of the right type. The field name list, if present, overrides
   * the original names of the fields.
   *
   * @param typeFactory Type factory
   * @param leftType Type of left input to join
   * @param rightType Type of right input to join
   * @param fieldNameList If not null, overrides the original names of the fields
   * @param systemFieldList List of system fields that will be prefixed to output row type;
   *     typically empty but must not be null
   * @return type of row which results when two relations are joined
   */
  public static RelDataType createJoinType(
      RelDataTypeFactory typeFactory,
      RelDataType leftType,
      RelDataType rightType,
      List<String> fieldNameList,
      List<RelDataTypeField> systemFieldList) {
    assert (fieldNameList == null)
        || (fieldNameList.size()
            == (systemFieldList.size() + leftType.getFieldCount() + rightType.getFieldCount()));
    List<String> nameList = new ArrayList<>();
    final List<RelDataType> typeList = new ArrayList<>();

    // Use a set to keep track of the field names; this is needed
    // to ensure that the contains() call to check for name uniqueness
    // runs in constant time; otherwise, if the number of fields is large,
    // doing a contains() on a list can be expensive.
    final Set<String> uniqueNameList =
        typeFactory.getTypeSystem().isSchemaCaseSensitive()
            ? new HashSet<String>()
            : new TreeSet<>(String.CASE_INSENSITIVE_ORDER);
    addFields(systemFieldList, typeList, nameList, uniqueNameList);
    addFields(leftType.getFieldList(), typeList, nameList, uniqueNameList);
    if (rightType != null) {
      addFields(rightType.getFieldList(), typeList, nameList, uniqueNameList);
    }
    if (fieldNameList != null) {
      assert fieldNameList.size() == nameList.size();
      nameList = fieldNameList;
    }
    return typeFactory.createStructType(typeList, nameList);
  }
    @Override
    public AggregateCall other(RelDataTypeFactory typeFactory, AggregateCall e) {

      return AggregateCall.create(
          new HiveSqlCountAggFunction(
              isDistinct, returnTypeInference, operandTypeInference, operandTypeChecker),
          false,
          ImmutableIntList.of(),
          -1,
          typeFactory.createTypeWithNullability(
              typeFactory.createSqlType(SqlTypeName.BIGINT), true),
          "count");
    }
Esempio n. 3
0
 private RexNode makeCastExactToInterval(RelDataType toType, RexNode exp) {
   IntervalSqlType intervalType = (IntervalSqlType) toType;
   TimeUnit endUnit = intervalType.getIntervalQualifier().getEndUnit();
   if (endUnit == null) {
     endUnit = intervalType.getIntervalQualifier().getStartUnit();
   }
   int scale = 0;
   if (endUnit == TimeUnit.SECOND) {
     scale =
         Math.min(
             intervalType
                 .getIntervalQualifier()
                 .getFractionalSecondPrecision(typeFactory.getTypeSystem()),
             3);
   }
   BigDecimal multiplier =
       BigDecimal.valueOf(endUnit.multiplier).divide(BigDecimal.TEN.pow(scale));
   RelDataType decimalType =
       getTypeFactory()
           .createSqlType(SqlTypeName.DECIMAL, scale + intervalType.getPrecision(), scale);
   RexNode value = decodeIntervalOrDecimal(ensureType(decimalType, exp, true));
   if (multiplier.longValue() != 1) {
     value = makeCall(SqlStdOperatorTable.MULTIPLY, value, makeExactLiteral(multiplier));
   }
   return encodeIntervalOrDecimal(value, toType, false);
 }
Esempio n. 4
0
 /**
  * Creates a RexBuilder.
  *
  * @param typeFactory Type factory
  */
 public RexBuilder(RelDataTypeFactory typeFactory) {
   this.typeFactory = typeFactory;
   this.booleanTrue =
       makeLiteral(
           Boolean.TRUE, typeFactory.createSqlType(SqlTypeName.BOOLEAN), SqlTypeName.BOOLEAN);
   this.booleanFalse =
       makeLiteral(
           Boolean.FALSE, typeFactory.createSqlType(SqlTypeName.BOOLEAN), SqlTypeName.BOOLEAN);
   this.charEmpty =
       makeLiteral(
           new NlsString("", null, null),
           typeFactory.createSqlType(SqlTypeName.CHAR, 0),
           SqlTypeName.CHAR);
   this.constantNull =
       makeLiteral(null, typeFactory.createSqlType(SqlTypeName.NULL), SqlTypeName.NULL);
 }
Esempio n. 5
0
 /** Creates a Timestamp literal. */
 public RexLiteral makeTimestampLiteral(Calendar timestamp, int precision) {
   assert timestamp != null;
   return makeLiteral(
       timestamp,
       typeFactory.createSqlType(SqlTypeName.TIMESTAMP, precision),
       SqlTypeName.TIMESTAMP);
 }
Esempio n. 6
0
 /** Creates an interval literal. */
 public RexLiteral makeIntervalLiteral(BigDecimal v, SqlIntervalQualifier intervalQualifier) {
   return makeLiteral(
       v,
       typeFactory.createSqlIntervalType(intervalQualifier),
       intervalQualifier.isYearMonth()
           ? SqlTypeName.INTERVAL_YEAR_MONTH
           : SqlTypeName.INTERVAL_DAY_TIME);
 }
Esempio n. 7
0
 /** Creates a double-precision literal. */
 public RexLiteral makeApproxLiteral(BigDecimal bd) {
   // Validator should catch if underflow is allowed
   // If underflow is allowed, let underflow become zero
   if (bd.doubleValue() == 0) {
     bd = BigDecimal.ZERO;
   }
   return makeApproxLiteral(bd, typeFactory.createSqlType(SqlTypeName.DOUBLE));
 }
Esempio n. 8
0
 /** Creates a numeric literal. */
 public RexLiteral makeExactLiteral(BigDecimal bd) {
   RelDataType relType;
   int scale = bd.scale();
   long l = bd.unscaledValue().longValue();
   assert scale >= 0;
   assert scale <= typeFactory.getTypeSystem().getMaxNumericScale() : scale;
   assert BigDecimal.valueOf(l, scale).equals(bd);
   if (scale == 0) {
     if ((l >= Integer.MIN_VALUE) && (l <= Integer.MAX_VALUE)) {
       relType = typeFactory.createSqlType(SqlTypeName.INTEGER);
     } else {
       relType = typeFactory.createSqlType(SqlTypeName.BIGINT);
     }
   } else {
     int precision = bd.unscaledValue().toString().length();
     relType = typeFactory.createSqlType(SqlTypeName.DECIMAL, scale, precision);
   }
   return makeExactLiteral(bd, relType);
 }
Esempio n. 9
0
  /** Creates a call to a windowed agg. */
  public RexNode makeOver(
      RelDataType type,
      SqlAggFunction operator,
      List<RexNode> exprs,
      List<RexNode> partitionKeys,
      ImmutableList<RexFieldCollation> orderKeys,
      RexWindowBound lowerBound,
      RexWindowBound upperBound,
      boolean physical,
      boolean allowPartial,
      boolean nullWhenCountZero) {
    assert operator != null;
    assert exprs != null;
    assert partitionKeys != null;
    assert orderKeys != null;
    final RexWindow window = makeWindow(partitionKeys, orderKeys, lowerBound, upperBound, physical);
    final RexOver over = new RexOver(type, operator, exprs, window);
    RexNode result = over;

    // This should be correct but need time to go over test results.
    // Also want to look at combing with section below.
    if (nullWhenCountZero) {
      final RelDataType bigintType = getTypeFactory().createSqlType(SqlTypeName.BIGINT);
      result =
          makeCall(
              SqlStdOperatorTable.CASE,
              makeCall(
                  SqlStdOperatorTable.GREATER_THAN,
                  new RexOver(bigintType, SqlStdOperatorTable.COUNT, exprs, window),
                  makeLiteral(BigDecimal.ZERO, bigintType, SqlTypeName.DECIMAL)),
              ensureType(
                  type, // SUM0 is non-nullable, thus need a cast
                  new RexOver(
                      typeFactory.createTypeWithNullability(type, false), operator, exprs, window),
                  false),
              makeCast(type, constantNull()));
    }
    if (!allowPartial) {
      Util.permAssert(physical, "DISALLOW PARTIAL over RANGE");
      final RelDataType bigintType = getTypeFactory().createSqlType(SqlTypeName.BIGINT);
      // todo: read bound
      result =
          makeCall(
              SqlStdOperatorTable.CASE,
              makeCall(
                  SqlStdOperatorTable.GREATER_THAN_OR_EQUAL,
                  new RexOver(
                      bigintType, SqlStdOperatorTable.COUNT, ImmutableList.<RexNode>of(), window),
                  makeLiteral(BigDecimal.valueOf(2), bigintType, SqlTypeName.DECIMAL)),
              result,
              constantNull);
    }
    return result;
  }
Esempio n. 10
0
 /**
  * Creates a character string literal with type CHAR and default charset and collation.
  *
  * @param s String value
  * @return Character string literal
  */
 protected RexLiteral makePreciseStringLiteral(String s) {
   assert s != null;
   if (s.equals("")) {
     return charEmpty;
   } else {
     return makeLiteral(
         new NlsString(s, null, null),
         typeFactory.createSqlType(SqlTypeName.CHAR, s.length()),
         SqlTypeName.CHAR);
   }
 }
Esempio n. 11
0
 public static RelDataType createTypeFromProjection(
     RelDataType type,
     List<String> columnNameList,
     RelDataTypeFactory typeFactory,
     boolean caseSensitive) {
   // If the names in columnNameList and type have case-sensitive differences,
   // the resulting type will use those from type. These are presumably more
   // canonical.
   final List<RelDataTypeField> fields = new ArrayList<>(columnNameList.size());
   for (String name : columnNameList) {
     RelDataTypeField field = type.getField(name, caseSensitive, false);
     fields.add(type.getFieldList().get(field.getIndex()));
   }
   return typeFactory.createStructType(fields);
 }
Esempio n. 12
0
 /**
  * Derives the type of a join relational expression.
  *
  * @param leftType Row type of left input to join
  * @param rightType Row type of right input to join
  * @param joinType Type of join
  * @param typeFactory Type factory
  * @param fieldNameList List of names of fields; if null, field names are inherited and made
  *     unique
  * @param systemFieldList List of system fields that will be prefixed to output row type;
  *     typically empty but must not be null
  * @return join type
  */
 public static RelDataType deriveJoinRowType(
     RelDataType leftType,
     RelDataType rightType,
     JoinRelType joinType,
     RelDataTypeFactory typeFactory,
     List<String> fieldNameList,
     List<RelDataTypeField> systemFieldList) {
   assert systemFieldList != null;
   switch (joinType) {
     case LEFT:
       rightType = typeFactory.createTypeWithNullability(rightType, true);
       break;
     case RIGHT:
       leftType = typeFactory.createTypeWithNullability(leftType, true);
       break;
     case FULL:
       leftType = typeFactory.createTypeWithNullability(leftType, true);
       rightType = typeFactory.createTypeWithNullability(rightType, true);
       break;
     default:
       break;
   }
   return createJoinType(typeFactory, leftType, rightType, fieldNameList, systemFieldList);
 }
Esempio n. 13
0
 /**
  * Internal method to create a call to a literal. Code outside this package should call one of the
  * type-specific methods such as {@link #makeDateLiteral(Calendar)}, {@link
  * #makeLiteral(boolean)}, {@link #makeLiteral(String)}.
  *
  * @param o Value of literal, must be appropriate for the type
  * @param type Type of literal
  * @param typeName SQL type of literal
  * @return Literal
  */
 protected RexLiteral makeLiteral(Comparable o, RelDataType type, SqlTypeName typeName) {
   // All literals except NULL have NOT NULL types.
   type = typeFactory.createTypeWithNullability(type, o == null);
   if (typeName == SqlTypeName.CHAR) {
     // Character literals must have a charset and collation. Populate
     // from the type if necessary.
     assert o instanceof NlsString;
     NlsString nlsString = (NlsString) o;
     if ((nlsString.getCollation() == null) || (nlsString.getCharset() == null)) {
       assert type.getSqlTypeName() == SqlTypeName.CHAR;
       assert type.getCharset().name() != null;
       assert type.getCollation() != null;
       o = new NlsString(nlsString.getValue(), type.getCharset().name(), type.getCollation());
     }
   }
   return new RexLiteral(o, type, typeName);
 }
Esempio n. 14
0
  public RelDataType createSqlType(RelDataTypeFactory typeFactory) {
    BitString bitString;
    switch (typeName) {
      case NULL:
      case BOOLEAN:
        RelDataType ret = typeFactory.createSqlType(typeName);
        ret = typeFactory.createTypeWithNullability(ret, null == value);
        return ret;
      case BINARY:
        bitString = (BitString) value;
        int bitCount = bitString.getBitCount();
        return typeFactory.createSqlType(SqlTypeName.BINARY, bitCount / 8);
      case CHAR:
        NlsString string = (NlsString) value;
        Charset charset = string.getCharset();
        if (null == charset) {
          charset = typeFactory.getDefaultCharset();
        }
        SqlCollation collation = string.getCollation();
        if (null == collation) {
          collation = SqlCollation.COERCIBLE;
        }
        RelDataType type = typeFactory.createSqlType(SqlTypeName.CHAR, string.getValue().length());
        type = typeFactory.createTypeWithCharsetAndCollation(type, charset, collation);
        return type;

      case INTERVAL_YEAR_MONTH:
      case INTERVAL_DAY_TIME:
        SqlIntervalLiteral.IntervalValue intervalValue = (SqlIntervalLiteral.IntervalValue) value;
        return typeFactory.createSqlIntervalType(intervalValue.getIntervalQualifier());

      case SYMBOL:
        return typeFactory.createSqlType(SqlTypeName.SYMBOL);

      case INTEGER: // handled in derived class
      case TIME: // handled in derived class
      case VARCHAR: // should never happen
      case VARBINARY: // should never happen

      default:
        throw Util.needToImplement(toString() + ", operand=" + value);
    }
  }
Esempio n. 15
0
 private RelDataType guessType(Object value) {
   if (value == null) {
     return typeFactory.createSqlType(SqlTypeName.NULL);
   }
   if (value instanceof Float || value instanceof Double) {
     return typeFactory.createSqlType(SqlTypeName.DOUBLE);
   }
   if (value instanceof Number) {
     return typeFactory.createSqlType(SqlTypeName.BIGINT);
   }
   if (value instanceof Boolean) {
     return typeFactory.createSqlType(SqlTypeName.BOOLEAN);
   }
   if (value instanceof String) {
     return typeFactory.createSqlType(SqlTypeName.CHAR, ((String) value).length());
   }
   if (value instanceof ByteString) {
     return typeFactory.createSqlType(SqlTypeName.BINARY, ((ByteString) value).length());
   }
   throw new AssertionError("unknown type " + value.getClass());
 }
Esempio n. 16
0
 /**
  * Creates a reference to all the fields in the row.
  *
  * <p>For example, if the input row has type <code>T{f0,f1,f2,f3,f4}</code> then <code>
  * makeRangeReference(T{f0,f1,f2,f3,f4}, S{f3,f4}, 3)</code> is an expression which yields the
  * last 2 fields.
  *
  * @param type Type of the resulting range record.
  * @param offset Index of first field.
  * @param nullable Whether the record is nullable.
  */
 public RexRangeRef makeRangeReference(RelDataType type, int offset, boolean nullable) {
   if (nullable && !type.isNullable()) {
     type = typeFactory.createTypeWithNullability(type, nullable);
   }
   return new RexRangeRef(type, offset);
 }
Esempio n. 17
0
 /** Creates a Time literal. */
 public RexLiteral makeTimeLiteral(Calendar time, int precision) {
   assert time != null;
   return makeLiteral(
       time, typeFactory.createSqlType(SqlTypeName.TIME, precision), SqlTypeName.TIME);
 }
Esempio n. 18
0
 /** Creates a Date literal. */
 public RexLiteral makeDateLiteral(Calendar date) {
   assert date != null;
   return makeLiteral(date, typeFactory.createSqlType(SqlTypeName.DATE), SqlTypeName.DATE);
 }
Esempio n. 19
0
 /** Creates a byte array literal. */
 public RexLiteral makeBinaryLiteral(ByteString byteString) {
   return makeLiteral(
       byteString,
       typeFactory.createSqlType(SqlTypeName.BINARY, byteString.length()),
       SqlTypeName.BINARY);
 }
Esempio n. 20
0
 /** Creates a BIGINT literal. */
 public RexLiteral makeBigintLiteral(BigDecimal bd) {
   RelDataType bigintType = typeFactory.createSqlType(SqlTypeName.BIGINT);
   return makeLiteral(bd, bigintType, SqlTypeName.DECIMAL);
 }
Esempio n. 21
0
 /**
  * Casts a decimal's integer representation to a decimal node. If the expression is not the
  * expected integer type, then it is casted first.
  *
  * <p>An overflow check may be requested to ensure the internal value does not exceed the maximum
  * value of the decimal type.
  *
  * @param value integer representation of decimal
  * @param type type integer will be reinterpreted as
  * @param checkOverflow indicates whether an overflow check is required when reinterpreting this
  *     particular value as the decimal type. A check usually not required for arithmetic, but is
  *     often required for rounding and explicit casts.
  * @return the integer reinterpreted as an opaque decimal type
  */
 public RexNode encodeIntervalOrDecimal(RexNode value, RelDataType type, boolean checkOverflow) {
   RelDataType bigintType = typeFactory.createSqlType(SqlTypeName.BIGINT);
   RexNode cast = ensureType(bigintType, value, true);
   return makeReinterpretCast(type, cast, makeLiteral(checkOverflow));
 }
Esempio n. 22
0
 /**
  * Retrieves an interval or decimal node's integer representation
  *
  * @param node the interval or decimal value as an opaque type
  * @return an integer representation of the decimal value
  */
 public RexNode decodeIntervalOrDecimal(RexNode node) {
   assert SqlTypeUtil.isDecimal(node.getType()) || SqlTypeUtil.isInterval(node.getType());
   RelDataType bigintType = typeFactory.createSqlType(SqlTypeName.BIGINT);
   return makeReinterpretCast(matchNullability(bigintType, node), node, makeLiteral(false));
 }
Esempio n. 23
0
 /**
  * Creates a literal whose value is NULL, with a particular type.
  *
  * <p>The typing is necessary because RexNodes are strictly typed. For example, in the Rex world
  * the <code>NULL</code> parameter to <code>
  * SUBSTRING(NULL FROM 2 FOR 4)</code> must have a valid VARCHAR type so that the result type can
  * be determined.
  *
  * @param typeName Type to cast NULL to
  * @return NULL literal of given type
  */
 public RexNode makeNullLiteral(SqlTypeName typeName) {
   RelDataType type =
       typeFactory.createTypeWithNullability(typeFactory.createSqlType(typeName), true);
   return makeCast(type, constantNull());
 }
Esempio n. 24
0
 /**
  * Creates a literal representing a flag.
  *
  * @param flag Flag value
  */
 public RexLiteral makeFlag(Enum flag) {
   assert flag != null;
   return makeLiteral(flag, typeFactory.createSqlType(SqlTypeName.SYMBOL), SqlTypeName.SYMBOL);
 }
Esempio n. 25
0
 /**
  * Creates a literal of a given type. The value is assumed to be compatible with the type.
  *
  * @param value Value
  * @param type Type
  * @param allowCast Whether to allow a cast. If false, value is always a {@link RexLiteral} but
  *     may not be the exact type
  * @return Simple literal, or cast simple literal
  */
 public RexNode makeLiteral(Object value, RelDataType type, boolean allowCast) {
   if (value == null) {
     return makeCast(type, constantNull);
   }
   if (type.isNullable()) {
     final RelDataType typeNotNull = typeFactory.createTypeWithNullability(type, false);
     RexNode literalNotNull = makeLiteral(value, typeNotNull, allowCast);
     return makeAbstractCast(type, literalNotNull);
   }
   value = clean(value, type);
   RexLiteral literal;
   final List<RexNode> operands;
   switch (type.getSqlTypeName()) {
     case CHAR:
       return makeCharLiteral(padRight((NlsString) value, type.getPrecision()));
     case VARCHAR:
       literal = makeCharLiteral((NlsString) value);
       if (allowCast) {
         return makeCast(type, literal);
       } else {
         return literal;
       }
     case BINARY:
       return makeBinaryLiteral(padRight((ByteString) value, type.getPrecision()));
     case VARBINARY:
       literal = makeBinaryLiteral((ByteString) value);
       if (allowCast) {
         return makeCast(type, literal);
       } else {
         return literal;
       }
     case TINYINT:
     case SMALLINT:
     case INTEGER:
     case BIGINT:
     case DECIMAL:
       return makeExactLiteral((BigDecimal) value, type);
     case FLOAT:
     case REAL:
     case DOUBLE:
       return makeApproxLiteral((BigDecimal) value, type);
     case BOOLEAN:
       return (Boolean) value ? booleanTrue : booleanFalse;
     case TIME:
       return makeTimeLiteral((Calendar) value, type.getPrecision());
     case DATE:
       return makeDateLiteral((Calendar) value);
     case TIMESTAMP:
       return makeTimestampLiteral((Calendar) value, type.getPrecision());
     case INTERVAL_YEAR_MONTH:
     case INTERVAL_DAY_TIME:
       return makeIntervalLiteral((BigDecimal) value, type.getIntervalQualifier());
     case MAP:
       final MapSqlType mapType = (MapSqlType) type;
       @SuppressWarnings("unchecked")
       final Map<Object, Object> map = (Map) value;
       operands = new ArrayList<RexNode>();
       for (Map.Entry<Object, Object> entry : map.entrySet()) {
         operands.add(makeLiteral(entry.getKey(), mapType.getKeyType(), allowCast));
         operands.add(makeLiteral(entry.getValue(), mapType.getValueType(), allowCast));
       }
       return makeCall(SqlStdOperatorTable.MAP_VALUE_CONSTRUCTOR, operands);
     case ARRAY:
       final ArraySqlType arrayType = (ArraySqlType) type;
       @SuppressWarnings("unchecked")
       final List<Object> listValue = (List) value;
       operands = new ArrayList<RexNode>();
       for (Object entry : listValue) {
         operands.add(makeLiteral(entry, arrayType.getComponentType(), allowCast));
       }
       return makeCall(SqlStdOperatorTable.ARRAY_VALUE_CONSTRUCTOR, operands);
     case ANY:
       return makeLiteral(value, guessType(value), allowCast);
     default:
       throw Util.unexpected(type.getSqlTypeName());
   }
 }
  @Override
  public Prel visitProject(ProjectPrel project, Object unused) throws RelConversionException {

    // Apply the rule to the child
    RelNode originalInput = ((Prel) project.getInput(0)).accept(this, null);
    project = (ProjectPrel) project.copy(project.getTraitSet(), Lists.newArrayList(originalInput));

    List<RexNode> exprList = new ArrayList<>();

    List<RelDataTypeField> relDataTypes = new ArrayList();
    List<RelDataTypeField> origRelDataTypes = new ArrayList();
    int i = 0;
    final int lastColumnReferenced = PrelUtil.getLastUsedColumnReference(project.getProjects());

    if (lastColumnReferenced == -1) {
      return project;
    }

    final int lastRexInput = lastColumnReferenced + 1;
    RexVisitorComplexExprSplitter exprSplitter =
        new RexVisitorComplexExprSplitter(factory, funcReg, lastRexInput);

    for (RexNode rex : project.getChildExps()) {
      origRelDataTypes.add(project.getRowType().getFieldList().get(i));
      i++;
      exprList.add(rex.accept(exprSplitter));
    }
    List<RexNode> complexExprs = exprSplitter.getComplexExprs();

    if (complexExprs.size() == 1 && findTopComplexFunc(project.getChildExps()).size() == 1) {
      return project;
    }

    ProjectPrel childProject;

    List<RexNode> allExprs = new ArrayList();
    int exprIndex = 0;
    List<String> fieldNames = originalInput.getRowType().getFieldNames();
    for (int index = 0; index < lastRexInput; index++) {
      RexBuilder builder = new RexBuilder(factory);
      allExprs.add(
          builder.makeInputRef(new RelDataTypeDrillImpl(new RelDataTypeHolder(), factory), index));

      if (fieldNames.get(index).contains(StarColumnHelper.STAR_COLUMN)) {
        relDataTypes.add(
            new RelDataTypeFieldImpl(
                fieldNames.get(index), allExprs.size(), factory.createSqlType(SqlTypeName.ANY)));
      } else {
        relDataTypes.add(
            new RelDataTypeFieldImpl(
                "EXPR$" + exprIndex, allExprs.size(), factory.createSqlType(SqlTypeName.ANY)));
        exprIndex++;
      }
    }
    RexNode currRexNode;
    int index = lastRexInput - 1;

    // if the projection expressions contained complex outputs, split them into their own individual
    // projects
    if (complexExprs.size() > 0) {
      while (complexExprs.size() > 0) {
        if (index >= lastRexInput) {
          allExprs.remove(allExprs.size() - 1);
          RexBuilder builder = new RexBuilder(factory);
          allExprs.add(
              builder.makeInputRef(
                  new RelDataTypeDrillImpl(new RelDataTypeHolder(), factory), index));
        }
        index++;
        exprIndex++;

        currRexNode = complexExprs.remove(0);
        allExprs.add(currRexNode);
        relDataTypes.add(
            new RelDataTypeFieldImpl(
                "EXPR$" + exprIndex, allExprs.size(), factory.createSqlType(SqlTypeName.ANY)));
        childProject =
            new ProjectPrel(
                project.getCluster(),
                project.getTraitSet(),
                originalInput,
                ImmutableList.copyOf(allExprs),
                new RelRecordType(relDataTypes));
        originalInput = childProject;
      }
      // copied from above, find a better way to do this
      allExprs.remove(allExprs.size() - 1);
      RexBuilder builder = new RexBuilder(factory);
      allExprs.add(
          builder.makeInputRef(new RelDataTypeDrillImpl(new RelDataTypeHolder(), factory), index));
      relDataTypes.add(
          new RelDataTypeFieldImpl(
              "EXPR$" + index, allExprs.size(), factory.createSqlType(SqlTypeName.ANY)));
    }
    return (Prel)
        project.copy(
            project.getTraitSet(), originalInput, exprList, new RelRecordType(origRelDataTypes));
  }