/**
     * Casts the rhs to an {@link AssignableValue} using that interface's standard assignment
     * method. i.e.
     *
     * <pre>
     * [AssignableValueType] lhs;
     * lhs.[assignMethod](rhs);
     * </pre>
     *
     * or perhaps a type-specific cast:
     *
     * <pre>
     * [AssignableValueType] lhs;
     * lhs.[castMethod](rhs, lhs.getPrecision());
     * </pre>
     *
     * <p>Code is also generated to pad and truncate values which need special handling, such as
     * date and time types. Plus good old null handling.
     */
    private Expression castToAssignableValue() {
      ensureLhs();

      if (requiresSpecializedCast() && rhsType.isNullable()) {
        assert (lhsType.isNullable());

        // propagate null value; normally, we can rely on
        // assignFrom to do it for us, but for specialized casts,
        // we can't
        Expression nullTest =
            new MethodCall(rhsExp, NullableValue.NULL_IND_ACCESSOR_NAME, new ExpressionList());
        addStatement(
            new ExpressionStatement(
                new MethodCall(
                    lhsExp, NullableValue.NULL_IND_MUTATOR_NAME, new ExpressionList(nullTest))));
        StatementList ifStmtList = new StatementList();
        addStatement(new IfStatement(not(nullTest), ifStmtList));
        borrowStmtList(ifStmtList);
        try {
          return castToAssignableValueImpl();
        } finally {
          returnStmtList(ifStmtList);
        }
      } else {
        return castToAssignableValueImpl();
      }
    }
Пример #2
0
 public int getFieldJdbcType(int fieldOrdinal) {
   RelDataType type = getFieldNamedType(fieldOrdinal);
   SqlTypeName typeName = type.getSqlTypeName();
   if (typeName == null) {
     return Types.OTHER;
   }
   return typeName.getJdbcOrdinal();
 }
Пример #3
0
 public RelDataType getFieldType(int fieldOrdinal) {
   RelDataType namedType = getFieldNamedType(fieldOrdinal);
   if (namedType.getSqlTypeName() == SqlTypeName.DISTINCT) {
     // for most metadata calls, report information about the
     // predefined type on which the distinct type is based
     return namedType.getFields()[0].getType();
   } else {
     return namedType;
   }
 }
 /**
  * Gets the right hand expression as a valid value to be assigned to the left hand side. Usually
  * returns the original rhs. However, if the lhs is of a primitive type, and the rhs is an
  * explicit null, returns a primitive value instead.
  */
 private Expression rhsAsValue() {
   if (SqlTypeUtil.isJavaPrimitive(lhsType) && (rhsType.getSqlTypeName() == SqlTypeName.NULL)) {
     if (lhsType.getSqlTypeName() == SqlTypeName.BOOLEAN) {
       return Literal.constantFalse();
     } else {
       return Literal.constantZero();
     }
   }
   return rhsExp;
 }
 /** Generates code to throw an exception when a NULL value is casted to a NOT NULL type */
 private void checkNotNull() {
   if (!lhsType.isNullable() && rhsType.isNullable()) {
     rhsExp = rhsAsJava();
     addStatement(
         new ExpressionStatement(
             new MethodCall(
                 translator.getRelImplementor().getConnectionVariable(),
                 "checkNotNull",
                 new ExpressionList(Literal.makeLiteral(targetName), rhsExp))));
   }
 }
Пример #6
0
 /**
  * Generates a cast from one row type to another
  *
  * @param rexBuilder RexBuilder to use for constructing casts
  * @param lhsRowType target row type
  * @param rhsRowType source row type; fields must be 1-to-1 with lhsRowType, in same order
  * @return cast expressions
  */
 public static RexNode[] generateCastExpressions(
     RexBuilder rexBuilder, RelDataType lhsRowType, RelDataType rhsRowType) {
   int n = rhsRowType.getFieldCount();
   assert n == lhsRowType.getFieldCount()
       : "field count: lhs [" + lhsRowType + "] rhs [" + rhsRowType + "]";
   RexNode[] rhsExps = new RexNode[n];
   for (int i = 0; i < n; ++i) {
     rhsExps[i] = rexBuilder.makeInputRef(rhsRowType.getFields()[i].getType(), i);
   }
   return generateCastExpressions(rexBuilder, lhsRowType, rhsExps);
 }
Пример #7
0
 public int getFieldScale(int fieldOrdinal) {
   RelDataType type = getFieldType(fieldOrdinal);
   SqlTypeName typeName = type.getSqlTypeName();
   if (typeName == null) {
     return 0;
   }
   if (typeName.allowsPrecScale(true, true)) {
     return type.getScale();
   } else {
     return 0;
   }
 }
Пример #8
0
 /**
  * Derives the list of column names suitable for NATURAL JOIN. These are the columns that occur
  * exactly once on each side of the join.
  *
  * @param leftRowType Row type of left input to the join
  * @param rightRowType Row type of right input to the join
  * @return List of columns that occur once on each side
  */
 public static List<String> deriveNaturalJoinColumnList(
     RelDataType leftRowType, RelDataType rightRowType) {
   List<String> naturalColumnNames = new ArrayList<String>();
   final List<String> leftNames = leftRowType.getFieldNames();
   final List<String> rightNames = rightRowType.getFieldNames();
   for (String name : leftNames) {
     if ((Collections.frequency(leftNames, name) == 1)
         && (Collections.frequency(rightNames, name) == 1)) {
       naturalColumnNames.add(name);
     }
   }
   return naturalColumnNames;
 }
Пример #9
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<RelDataTypeField>(columnNameList.size());
   for (String name : columnNameList) {
     RelDataTypeField field = type.getField(name, caseSensitive);
     fields.add(type.getFieldList().get(field.getIndex()));
   }
   return typeFactory.createStructType(fields);
 }
Пример #10
0
 public RelDataType getNamedType(SqlIdentifier typeName) {
   if (typeName.equalsDeep(addressType.getSqlIdentifier(), false)) {
     return addressType;
   } else {
     return null;
   }
 }
    /**
     * Implements a cast from any Java primitive to a nullable Java primitive as a simple
     * assignment. i.e.
     *
     * <pre>
     * [NullablePrimitiveType] lhs;
     * lhs.[nullIndicator] = ...;
     * if (! lhs.[nullIndicator]) {
     *     // check overflow ...
     *     // round ...
     *     lhs.[value] = ...;
     * }
     * </pre>
     */
    private Expression castPrimitiveToNullablePrimitive() {
      ensureLhs();
      boolean nullableSource = rhsType.isNullable();
      Expression rhsIsNull;
      if (nullableSource) {
        rhsIsNull = getNullIndicator(rhsExp);
        rhsExp = getValue(rhsType, rhsExp);
      } else {
        rhsIsNull = Literal.constantFalse();
      }

      addStatement(assign(getNullIndicator(lhsExp), rhsIsNull));
      StatementList setValueBlock = new StatementList();
      StatementList oldList = borrowStmtList(setValueBlock);
      try {
        checkOverflow();
        roundAsNeeded();
        addStatement(assign(getValue(lhsType, lhsExp), new CastExpression(getLhsClass(), rhsExp)));
      } finally {
        returnStmtList(oldList);
      }
      if (nullableSource) {
        addStatement(new IfStatement(not(getNullIndicator(lhsExp)), setValueBlock));
      } else {
        addStatementList(setValueBlock);
      }
      return lhsExp;
    }
Пример #12
0
 public Void visitInputRef(RexInputRef inputRef) {
   super.visitInputRef(inputRef);
   if (inputRef.getIndex() >= inputRowType.getFieldCount()) {
     throw new IllegalForwardRefException();
   }
   return null;
 }
Пример #13
0
 public String getFieldTypeName(int fieldOrdinal) {
   RelDataType type = getFieldNamedType(fieldOrdinal);
   SqlTypeName typeName = type.getSqlTypeName();
   if (typeName == null) {
     return type.toString();
   }
   switch (typeName) {
     case STRUCTURED:
     case DISTINCT:
       return type.getSqlIdentifier().toString();
     case INTERVAL_DAY_TIME:
     case INTERVAL_YEAR_MONTH:
       return type.toString();
   }
   return typeName.name();
 }
Пример #14
0
 /** Creates an array of {@link RexLocalRef} objects, one for each field of a given rowtype. */
 public static RexLocalRef[] toLocalRefs(RelDataType rowType) {
   final RelDataTypeField[] fields = rowType.getFields();
   final RexLocalRef[] refs = new RexLocalRef[fields.length];
   for (int i = 0; i < refs.length; i++) {
     refs[i] = new RexLocalRef(i, fields[i].getType());
   }
   return refs;
 }
    /**
     * Checks for overflow when assigning one primitive type to another. Non-primitive types check
     * for overflow during assignment.
     */
    private void checkOverflow() {
      String maxLiteral = null;
      String minLiteral = null;
      if (lhsType == null) {
        return;
      }

      // Assume that equivalent types can be assigned without overflow
      if (lhsType.getSqlTypeName() == rhsType.getSqlTypeName()) {
        return;
      }

      // Approximate numerics have a wider range than exact numerics
      if (SqlTypeUtil.isApproximateNumeric(lhsType) && SqlTypeUtil.isExactNumeric(rhsType)) {
        return;
      }

      // We can skip an error check if the left type is "larger"
      if (SqlTypeUtil.isIntType(lhsType)
          && SqlTypeUtil.isIntType(rhsType)
          && (SqlTypeUtil.maxValue(lhsType) >= SqlTypeUtil.maxValue(rhsType))) {
        return;
      }
      if (SqlTypeUtil.isExactNumeric(lhsType)) {
        String numClassName = SqlTypeUtil.getNumericJavaClassName(lhsType);
        minLiteral = numClassName + ".MIN_VALUE";
        maxLiteral = numClassName + ".MAX_VALUE";
      } else if (SqlTypeUtil.isApproximateNumeric(lhsType)) {
        String numClassName = SqlTypeUtil.getNumericJavaClassName(lhsType);
        maxLiteral = numClassName + ".MAX_VALUE";
        minLiteral = "-" + maxLiteral;
      }
      if (maxLiteral == null) {
        return;
      }
      Statement ifstmt =
          new IfStatement(
              new BinaryExpression(
                  new BinaryExpression(
                      rhsExp, BinaryExpression.LESS, new Literal(Literal.STRING, minLiteral)),
                  BinaryExpression.LOGICAL_OR,
                  new BinaryExpression(
                      rhsExp, BinaryExpression.GREATER, new Literal(Literal.STRING, maxLiteral))),
              getThrowStmtList());
      addStatement(ifstmt);
    }
Пример #16
0
 /**
  * Creates a FarragoJdbcMetaDataImpl.
  *
  * @param rowType Type info to return
  * @param fieldOrigins Origin of each field in column of catalog object
  */
 protected FarragoJdbcMetaDataImpl(RelDataType rowType, List<List<String>> fieldOrigins) {
   this.rowType = rowType;
   this.fieldOrigins = fieldOrigins;
   assert rowType != null;
   assert fieldOrigins != null;
   assert fieldOrigins.size() == rowType.getFieldCount()
       : "field origins " + fieldOrigins + " have different count than row type " + rowType;
 }
Пример #17
0
 /**
  * Creates an array of {@link RexInputRef} objects, one for each field of a given rowtype,
  * according to a permutation.
  *
  * @param args Permutation
  * @param rowType Input row type
  * @return Array of input refs
  */
 public static RexInputRef[] toInputRefs(int[] args, RelDataType rowType) {
   final RelDataTypeField[] fields = rowType.getFields();
   final RexInputRef[] rexNodes = new RexInputRef[args.length];
   for (int i = 0; i < args.length; i++) {
     int fieldOrdinal = args[i];
     rexNodes[i] = new RexInputRef(fieldOrdinal, fields[fieldOrdinal].getType());
   }
   return rexNodes;
 }
Пример #18
0
 /**
  * Generates a cast for a row type.
  *
  * @param rexBuilder RexBuilder to use for constructing casts
  * @param lhsRowType target row type
  * @param rhsExps expressions to be cast
  * @return cast expressions
  */
 public static RexNode[] generateCastExpressions(
     RexBuilder rexBuilder, RelDataType lhsRowType, RexNode[] rhsExps) {
   RelDataTypeField[] lhsFields = lhsRowType.getFields();
   final int fieldCount = lhsFields.length;
   RexNode[] castExps = new RexNode[fieldCount];
   assert fieldCount == rhsExps.length;
   for (int i = 0; i < fieldCount; ++i) {
     RelDataTypeField lhsField = lhsFields[i];
     RelDataType lhsType = lhsField.getType();
     RelDataType rhsType = rhsExps[i].getType();
     if (lhsType.equals(rhsType)) {
       castExps[i] = rhsExps[i];
     } else {
       castExps[i] = rexBuilder.makeCast(lhsType, rhsExps[i]);
     }
   }
   return castExps;
 }
Пример #19
0
 private SqlOperator toOp(SqlIdentifier name, Function function) {
   List<RelDataType> argTypes = new ArrayList<RelDataType>();
   List<SqlTypeFamily> typeFamilies = new ArrayList<SqlTypeFamily>();
   for (FunctionParameter o : function.getParameters()) {
     final RelDataType type = o.getType(typeFactory);
     argTypes.add(type);
     typeFamilies.add(Util.first(type.getSqlTypeName().getFamily(), SqlTypeFamily.ANY));
   }
   final RelDataType returnType;
   if (function instanceof ScalarFunction) {
     return new SqlUserDefinedFunction(
         name,
         ReturnTypes.explicit(Schemas.proto((ScalarFunction) function)),
         InferTypes.explicit(argTypes),
         OperandTypes.family(typeFamilies),
         toSql(argTypes),
         function);
   } else if (function instanceof AggregateFunction) {
     returnType = ((AggregateFunction) function).getReturnType(typeFactory);
     return new SqlUserDefinedAggFunction(
         name,
         ReturnTypes.explicit(returnType),
         InferTypes.explicit(argTypes),
         OperandTypes.family(typeFamilies),
         (AggregateFunction) function);
   } else if (function instanceof TableMacro) {
     return new SqlUserDefinedTableMacro(
         name,
         ReturnTypes.CURSOR,
         InferTypes.explicit(argTypes),
         OperandTypes.family(typeFamilies),
         (TableMacro) function);
   } else if (function instanceof TableFunction) {
     return new SqlUserDefinedTableFunction(
         name,
         ReturnTypes.CURSOR,
         InferTypes.explicit(argTypes),
         OperandTypes.family(typeFamilies),
         toSql(argTypes),
         (TableFunction) function);
   } else {
     throw new AssertionError("unknown function type " + function);
   }
 }
Пример #20
0
  /**
   * Determines whether a {@link RexCall} requires decimal expansion. It usually requires expansion
   * if it has decimal operands.
   *
   * <p>Exceptions to this rule are:
   *
   * <ul>
   *   <li>isNull doesn't require expansion
   *   <li>It's okay to cast decimals to and from char types
   *   <li>It's okay to cast nulls as decimals
   *   <li>Casts require expansion if their return type is decimal
   *   <li>Reinterpret casts can handle a decimal operand
   * </ul>
   *
   * @param expr expression possibly in need of expansion
   * @param recurse whether to check nested calls
   * @return whether the expression requires expansion
   */
  public static boolean requiresDecimalExpansion(RexNode expr, boolean recurse) {
    if (!(expr instanceof RexCall)) {
      return false;
    }
    RexCall call = (RexCall) expr;

    boolean localCheck = true;
    switch (call.getKind()) {
      case Reinterpret:
      case IsNull:
        localCheck = false;
        break;
      case Cast:
        RelDataType lhsType = call.getType();
        RelDataType rhsType = call.operands[0].getType();
        if (rhsType.getSqlTypeName() == SqlTypeName.NULL) {
          return false;
        }
        if (SqlTypeUtil.inCharFamily(lhsType) || SqlTypeUtil.inCharFamily(rhsType)) {
          localCheck = false;
        } else if (SqlTypeUtil.isDecimal(lhsType) && (lhsType != rhsType)) {
          return true;
        }
        break;
      default:
        localCheck = call.getOperator().requiresDecimalExpansion();
    }

    if (localCheck) {
      if (SqlTypeUtil.isDecimal(call.getType())) {
        // NOTE jvs 27-Mar-2007: Depending on the type factory, the
        // result of a division may be decimal, even though both inputs
        // are integer.
        return true;
      }
      for (int i = 0; i < call.operands.length; i++) {
        if (SqlTypeUtil.isDecimal(call.operands[i].getType())) {
          return true;
        }
      }
    }
    return (recurse && requiresDecimalExpansion(call.operands, recurse));
  }
 private boolean requiresSpecializedCast() {
   if ((rhsType != null)
       && (SqlTypeUtil.isNumeric(rhsType) || (rhsType.getSqlTypeName() == SqlTypeName.BOOLEAN))
       && SqlTypeUtil.inCharOrBinaryFamilies(lhsType)
       && !SqlTypeUtil.isLob(lhsType)) {
     // Boolean or Numeric to String.
     // sometimes the Integer got slipped by.
     return true;
   } else {
     return false;
   }
 }
Пример #22
0
    private List<RelCollation> deduceMonotonicity(SqlValidatorTable table) {
      final RelDataType rowType = table.getRowType();
      final List<RelCollation> collationList = new ArrayList<RelCollation>();

      // Deduce which fields the table is sorted on.
      int i = -1;
      for (RelDataTypeField field : rowType.getFieldList()) {
        ++i;
        final SqlMonotonicity monotonicity = table.getMonotonicity(field.getName());
        if (monotonicity != SqlMonotonicity.NOT_MONOTONIC) {
          final RelFieldCollation.Direction direction =
              monotonicity.isDecreasing()
                  ? RelFieldCollation.Direction.DESCENDING
                  : RelFieldCollation.Direction.ASCENDING;
          collationList.add(
              RelCollationImpl.of(
                  new RelFieldCollation(
                      i, direction, RelFieldCollation.NullDirection.UNSPECIFIED)));
        }
      }
      return collationList;
    }
Пример #23
0
 public static void checkCharsetAndCollateConsistentIfCharType(RelDataType type) {
   // (every charset must have a default collation)
   if (SqlTypeUtil.inCharFamily(type)) {
     Charset strCharset = type.getCharset();
     Charset colCharset = type.getCollation().getCharset();
     assert null != strCharset;
     assert null != colCharset;
     if (!strCharset.equals(colCharset)) {
       if (false) {
         // todo: enable this checking when we have a charset to
         //   collation mapping
         throw new Error(
             type.toString()
                 + " was found to have charset '"
                 + strCharset.name()
                 + "' and a mismatched collation charset '"
                 + colCharset.name()
                 + "'");
       }
     }
   }
 }
Пример #24
0
 /**
  * Returns whether the type of an array of expressions is compatible with a struct type.
  *
  * @param exprs Array of expressions
  * @param type Type
  * @param fail Whether to fail if there is a mismatch
  * @return Whether every expression has the same type as the corresponding member of the struct
  *     type
  * @see RelOptUtil#eq(String, RelDataType, String, RelDataType, boolean)
  */
 public static boolean compatibleTypes(RexNode[] exprs, RelDataType type, boolean fail) {
   final RelDataTypeField[] fields = type.getFields();
   if (exprs.length != fields.length) {
     assert !fail : "rowtype mismatches expressions";
     return false;
   }
   for (int i = 0; i < fields.length; i++) {
     final RelDataType exprType = exprs[i].getType();
     final RelDataType fieldType = fields[i].getType();
     if (!RelOptUtil.eq("type1", exprType, "type2", fieldType, fail)) {
       return false;
     }
   }
   return true;
 }
  // implement FarragoOJRexImplementor
  public Expression implementFarrago(
      FarragoRexToOJTranslator translator, RexCall call, Expression[] operands) {
    RelDataType lhsType = call.getType();
    RelDataType rhsType = call.operands[0].getType();
    Expression rhsExp = operands[0];

    SqlTypeName lhsTypeName = lhsType.getSqlTypeName();
    if ((lhsTypeName == SqlTypeName.CURSOR) || (lhsTypeName == SqlTypeName.COLUMN_LIST)) {
      // Conversion should already have been taken care of outside.
      return rhsExp;
    }

    // NOTE jvs 19-Nov-2008:  In some cases (e.g. FRG-273) a cast
    // may be illegal at the SQL level, but allowable as part of
    // implementation, so don't try to enforce
    // SqlTypeUtil.canCastFrom here.  Anything which was supposed
    // to have been prevented should already have been caught
    // by the validator.

    CastHelper helper =
        new CastHelper(translator, null, call.toString(), lhsType, rhsType, null, rhsExp);

    return helper.implement();
  }
Пример #26
0
  /**
   * Looks up a field with a given name, returning null if not found.
   *
   * @param rowType Row type
   * @param columnName Field name
   * @return Field, or null if not found
   */
  public static RelDataTypeField lookupField(
      boolean caseSensitive, final RelDataType rowType, String columnName) {
    RelDataTypeField field = rowType.getField(columnName, caseSensitive);
    if (field != null) {
      return field;
    }

    // If record type is flagged as having "any field you ask for",
    // return a type. (TODO: Better way to mark accommodating types.)
    RelDataTypeField extra = RelDataTypeImpl.extra(rowType);
    if (extra != null) {
      return new RelDataTypeFieldImpl(columnName, -1, extra.getType());
    }
    return null;
  }
Пример #27
0
 /**
  * Replaces the operands of a call. The new operands' types must match the old operands' types.
  */
 public static RexCall replaceOperands(RexCall call, RexNode[] operands) {
   if (call.operands == operands) {
     return call;
   }
   for (int i = 0; i < operands.length; i++) {
     RelDataType oldType = call.operands[i].getType();
     RelDataType newType = operands[i].getType();
     if (!oldType.isNullable() && newType.isNullable()) {
       throw Util.newInternal("invalid nullability");
     }
     assert (oldType.toString().equals(newType.toString()));
   }
   return new RexCall(call.getType(), call.getOperator(), operands);
 }
Пример #28
0
 /**
  * Returns whether the leading edge of a given array of expressions is wholly {@link RexInputRef}
  * objects with types corresponding to the underlying datatype.
  */
 public static boolean containIdentity(RexNode[] exprs, RelDataType rowType, boolean fail) {
   final RelDataTypeField[] fields = rowType.getFields();
   if (exprs.length < fields.length) {
     assert !fail : "exprs/rowType length mismatch";
     return false;
   }
   for (int i = 0; i < fields.length; i++) {
     if (!(exprs[i] instanceof RexInputRef)) {
       assert !fail : "expr[" + i + "] is not a RexInputRef";
       return false;
     }
     RexInputRef inputRef = (RexInputRef) exprs[i];
     if (inputRef.getIndex() != i) {
       assert !fail : "expr[" + i + "] has ordinal " + inputRef.getIndex();
       return false;
     }
     if (!RelOptUtil.eq("type1", exprs[i].getType(), "type2", fields[i].getType(), fail)) {
       return false;
     }
   }
   return true;
 }
    /**
     * Implement the cast expression.
     *
     * <p>TODO: check for overflow
     *
     * @return the rhs expression casted as the lhs type
     */
    public Expression implement() {
      // Check for invalid null assignment. Code generated afterwards
      // can assume null will never be assigned to a not null value.
      checkNotNull();

      // Check for an explicit rhs null value. Code generated
      // afterwards need never check for an explicit null.
      if (rhsType.getSqlTypeName() == SqlTypeName.NULL) {
        if (lhsType.isNullable()) {
          return castFromNull();
        } else {
          // NOTE jvs 27-Jan-2005:  this code will never actually
          // be executed do to previous checkNotNull test, but
          // it still has to compile!
          return rhsAsValue();
        }
      }

      // Case when left hand side is a nullable primitive
      if (translator.isNullablePrimitive(lhsType)) {
        if (SqlTypeUtil.isJavaPrimitive(rhsType)
            && (!rhsType.isNullable() || translator.isNullablePrimitive(rhsType))) {
          return castPrimitiveToNullablePrimitive();
        }
        return castToAssignableValue();
      }

      // Case when left hand side is a not nullable primitive
      if (SqlTypeUtil.isJavaPrimitive(lhsType)) {
        return castToNotNullPrimitive();
      }

      // Case when left hand side is a structure
      if (lhsType.isStruct()) {
        assert (rhsType.isStruct());

        // TODO jvs 27-May-2004:  relax this assert and deal with
        // conversions, null checks, etc.
        assert (lhsType.equals(rhsType));

        return getDirectAssignment();
      }

      // Default is to treat non-primitives as AssignableValue
      return castToAssignableValue();
    }
Пример #30
0
 /**
  * Returns the "extra" field in a row type whose presence signals that fields will come into
  * existence just by asking for them.
  *
  * @param rowType Row type
  * @return The "extra" field, or null
  */
 public static RelDataTypeField extra(RelDataType rowType) {
   // Even in a case-insensitive connection, the name must be precisely
   // "_extra".
   return rowType.getField("_extra", true);
 }