Java Code Examples for org.apache.calcite.rel.type.RelDataTypeField#getName()

The following examples show how to use org.apache.calcite.rel.type.RelDataTypeField#getName() . You can vote up the ones you like or vote down the ones you don't like, and go to the original project or source file by following the links above each example. You may check out the related API usage on the sidebar.
Example 1
Source File: SqlValidatorUtil.java    From Bats with Apache License 2.0 6 votes vote down vote up
private static void addFields(List<RelDataTypeField> fieldList,
    List<RelDataType> typeList, List<String> nameList,
    Set<String> uniqueNames) {
  for (RelDataTypeField field : fieldList) {
    String name = field.getName();

    // Ensure that name is unique from all previous field names
    if (uniqueNames.contains(name)) {
      String nameBase = name;
      for (int j = 0;; j++) {
        name = nameBase + j;
        if (!uniqueNames.contains(name)) {
          break;
        }
      }
    }
    nameList.add(name);
    uniqueNames.add(name);
    typeList.add(field.getType());
  }
}
 
Example 2
Source File: RelNodeConvertor.java    From Mycat2 with GNU General Public License v3.0 6 votes vote down vote up
private static List<FieldType> getFields(RelNode relNode) {
    RelDataType rowType = relNode.getRowType();
    List<RelDataTypeField> fieldList = rowType.getFieldList();
    ArrayList<FieldType> fieldSchemas = new ArrayList<>(fieldList.size());
    for (RelDataTypeField relDataTypeField : fieldList) {
        String name = relDataTypeField.getName();
        RelDataType type = relDataTypeField.getType();
        SqlTypeName sqlTypeName = type.getSqlTypeName();
        boolean nullable = type.isNullable();
        Integer precision = null;
        Integer scale = null;
        if (sqlTypeName.allowsPrec()) {
            precision = type.getPrecision();
        }
        if (sqlTypeName.allowsScale()) {
            scale = type.getScale();
        }
        fieldSchemas.add(new FieldType(name, ExprExplain.type(sqlTypeName), nullable, precision, scale));
    }
    return fieldSchemas;
}
 
Example 3
Source File: CalcitePlanner.java    From herddb with Apache License 2.0 6 votes vote down vote up
private PlannerOp planValues(EnumerableValues op) {

        List<List<CompiledSQLExpression>> tuples = new ArrayList<>(op.getTuples().size());
        RelDataType rowType = op.getRowType();
        List<RelDataTypeField> fieldList = rowType.getFieldList();

        Column[] columns = new Column[fieldList.size()];
        for (ImmutableList<RexLiteral> tuple : op.getTuples()) {
            List<CompiledSQLExpression> row = new ArrayList<>(tuple.size());
            for (RexLiteral node : tuple) {
                CompiledSQLExpression exp = SQLExpressionCompiler.compileExpression(node);
                row.add(exp);
            }
            tuples.add(row);
        }
        int i = 0;
        String[] fieldNames = new String[fieldList.size()];
        for (RelDataTypeField field : fieldList) {
            Column col = Column.column(field.getName(), convertToHerdType(field.getType()));
            fieldNames[i] = field.getName();
            columns[i++] = col;
        }
        return new ValuesOp(manager.getNodeId(), fieldNames,
                columns, tuples);

    }
 
Example 4
Source File: SqlValidatorUtil.java    From calcite with Apache License 2.0 6 votes vote down vote up
private static void addFields(List<RelDataTypeField> fieldList,
    List<RelDataType> typeList, List<String> nameList,
    Set<String> uniqueNames) {
  for (RelDataTypeField field : fieldList) {
    String name = field.getName();

    // Ensure that name is unique from all previous field names
    if (uniqueNames.contains(name)) {
      String nameBase = name;
      for (int j = 0;; j++) {
        name = nameBase + j;
        if (!uniqueNames.contains(name)) {
          break;
        }
      }
    }
    nameList.add(name);
    uniqueNames.add(name);
    typeList.add(field.getType());
  }
}
 
Example 5
Source File: TableNamespace.java    From Bats with Apache License 2.0 5 votes vote down vote up
/**
 * Ensures that extended columns that have the same name as a base column also
 * have the same data-type.
 */
private void checkExtendedColumnTypes(SqlNodeList extendList) {
  final List<RelDataTypeField> extendedFields =
      SqlValidatorUtil.getExtendedColumns(
          validator.getTypeFactory(), table, extendList);
  final List<RelDataTypeField> baseFields =
      getBaseRowType().getFieldList();
  final Map<String, Integer> nameToIndex =
      SqlValidatorUtil.mapNameToIndex(baseFields);

  for (final RelDataTypeField extendedField : extendedFields) {
    final String extFieldName = extendedField.getName();
    if (nameToIndex.containsKey(extFieldName)) {
      final Integer baseIndex = nameToIndex.get(extFieldName);
      final RelDataType baseType = baseFields.get(baseIndex).getType();
      final RelDataType extType = extendedField.getType();

      if (!extType.equals(baseType)) {
        // Get the extended column node that failed validation.
        final SqlNode extColNode =
            Iterables.find(extendList.getList(),
                sqlNode -> sqlNode instanceof SqlIdentifier
                    && Util.last(((SqlIdentifier) sqlNode).names).equals(
                        extendedField.getName()));

        throw validator.getValidationErrorFunction().apply(extColNode,
            RESOURCE.typeNotAssignable(
                baseFields.get(baseIndex).getName(), baseType.getFullTypeString(),
                extendedField.getName(), extType.getFullTypeString()));
      }
    }
  }
}
 
Example 6
Source File: JournalledJdbcSchema.java    From calcite-sql-rewriter with Apache License 2.0 5 votes vote down vote up
@Override
RelProtoDataType getRelDataType(
		DatabaseMetaData metaData,
		String catalogName,
		String schemaName,
		String tableName
) throws SQLException {
	if (journalledTableKeys.containsKey(tableName)) {
		// 1: Find columns for journal table
		RelDataType relDataType = super
				.getRelDataType(metaData, catalogName, schemaName, journalNameFor(tableName))
				.apply(new SqlTypeFactoryImpl(RelDataTypeSystem.DEFAULT) {
					@Override
					public RelDataType copyType(RelDataType type) {
						return type;
					}
				});

		RelDataTypeFactory.FieldInfoBuilder fieldInfo = new SqlTypeFactoryImpl(RelDataTypeSystem.DEFAULT).builder();

		// 2: Filter out journal-implementation columns
		for (RelDataTypeField field : relDataType.getFieldList()) {
			String fieldName = field.getName();
			if (fieldName.equals(versionField) || fieldName.equals(subsequentVersionField)) {
				continue;
			}
			fieldInfo.add(field);
		}

		return RelDataTypeImpl.proto(fieldInfo.build());
	} else {
		return super.getRelDataType(metaData, catalogName, schemaName, tableName);
	}
}
 
Example 7
Source File: TableNamespace.java    From calcite with Apache License 2.0 5 votes vote down vote up
/**
 * Ensures that extended columns that have the same name as a base column also
 * have the same data-type.
 */
private void checkExtendedColumnTypes(SqlNodeList extendList) {
  final List<RelDataTypeField> extendedFields =
      SqlValidatorUtil.getExtendedColumns(validator, table, extendList);
  final List<RelDataTypeField> baseFields =
      getBaseRowType().getFieldList();
  final Map<String, Integer> nameToIndex =
      SqlValidatorUtil.mapNameToIndex(baseFields);

  for (final RelDataTypeField extendedField : extendedFields) {
    final String extFieldName = extendedField.getName();
    if (nameToIndex.containsKey(extFieldName)) {
      final Integer baseIndex = nameToIndex.get(extFieldName);
      final RelDataType baseType = baseFields.get(baseIndex).getType();
      final RelDataType extType = extendedField.getType();

      if (!extType.equals(baseType)) {
        // Get the extended column node that failed validation.
        final SqlNode extColNode =
            Iterables.find(extendList.getList(),
                sqlNode -> sqlNode instanceof SqlIdentifier
                    && Util.last(((SqlIdentifier) sqlNode).names).equals(
                        extendedField.getName()));

        throw validator.getValidationErrorFunction().apply(extColNode,
            RESOURCE.typeNotAssignable(
                baseFields.get(baseIndex).getName(), baseType.getFullTypeString(),
                extendedField.getName(), extType.getFullTypeString()));
      }
    }
  }
}
 
Example 8
Source File: SqlTypeUtil.java    From calcite with Apache License 2.0 5 votes vote down vote up
/**
 * Flattens a record type by recursively expanding any fields which are
 * themselves record types. For each record type, a representative null
 * value field is also prepended (with state NULL for a null value and FALSE
 * for non-null), and all component types are asserted to be nullable, since
 * SQL doesn't allow NOT NULL to be specified on attributes.
 *
 * @param typeFactory   factory which should produced flattened type
 * @param recordType    type with possible nesting
 * @param flatteningMap if non-null, receives map from unflattened ordinal
 *                      to flattened ordinal (must have length at least
 *                      recordType.getFieldList().size())
 * @return flattened equivalent
 */
public static RelDataType flattenRecordType(
    RelDataTypeFactory typeFactory,
    RelDataType recordType,
    int[] flatteningMap) {
  if (!recordType.isStruct()) {
    return recordType;
  }
  List<RelDataTypeField> fieldList = new ArrayList<>();
  boolean nested =
      flattenFields(
          typeFactory,
          recordType,
          fieldList,
          flatteningMap);
  if (!nested) {
    return recordType;
  }
  List<RelDataType> types = new ArrayList<>();
  List<String> fieldNames = new ArrayList<>();
  Map<String, Long> fieldCnt = fieldList.stream()
      .map(RelDataTypeField::getName)
      .collect(Collectors.groupingBy(Function.identity(), Collectors.counting()));
  int i = -1;
  for (RelDataTypeField field : fieldList) {
    ++i;
    types.add(field.getType());
    String oriFieldName = field.getName();
    // Patch up the field name with index if there are duplicates.
    // There is still possibility that the patched name conflicts with existing ones,
    // but that should be rare case.
    String fieldName = fieldCnt.get(oriFieldName) > 1
        ? oriFieldName + "_" + i
        : oriFieldName;
    fieldNames.add(fieldName);
  }
  return typeFactory.createStructType(types, fieldNames);
}
 
Example 9
Source File: IdentifierNamespace.java    From Bats with Apache License 2.0 4 votes vote down vote up
public RelDataType validateImpl(RelDataType targetRowType) {
  resolvedNamespace = Objects.requireNonNull(resolveImpl(id));
  if (resolvedNamespace instanceof TableNamespace) {
    SqlValidatorTable table = resolvedNamespace.getTable();
    if (validator.shouldExpandIdentifiers()) {
      // TODO:  expand qualifiers for column references also
      List<String> qualifiedNames = table.getQualifiedName();
      if (qualifiedNames != null) {
        // Assign positions to the components of the fully-qualified
        // identifier, as best we can. We assume that qualification
        // adds names to the front, e.g. FOO.BAR becomes BAZ.FOO.BAR.
        List<SqlParserPos> poses =
            new ArrayList<>(
                Collections.nCopies(
                    qualifiedNames.size(), id.getParserPosition()));
        int offset = qualifiedNames.size() - id.names.size();

        // Test offset in case catalog supports fewer qualifiers than catalog
        // reader.
        if (offset >= 0) {
          for (int i = 0; i < id.names.size(); i++) {
            poses.set(i + offset, id.getComponentParserPosition(i));
          }
        }
        id.setNames(qualifiedNames, poses);
      }
    }
  }

  RelDataType rowType = resolvedNamespace.getRowType();

  if (extendList != null) {
    if (!(resolvedNamespace instanceof TableNamespace)) {
      throw new RuntimeException("cannot convert");
    }
    resolvedNamespace =
        ((TableNamespace) resolvedNamespace).extend(extendList);
    rowType = resolvedNamespace.getRowType();
  }

  // Build a list of monotonic expressions.
  final ImmutableList.Builder<Pair<SqlNode, SqlMonotonicity>> builder =
      ImmutableList.builder();
  List<RelDataTypeField> fields = rowType.getFieldList();
  for (RelDataTypeField field : fields) {
    final String fieldName = field.getName();
    final SqlMonotonicity monotonicity =
        resolvedNamespace.getMonotonicity(fieldName);
    if (monotonicity != SqlMonotonicity.NOT_MONOTONIC) {
      builder.add(
          Pair.of((SqlNode) new SqlIdentifier(fieldName, SqlParserPos.ZERO),
              monotonicity));
    }
  }
  monotonicExprs = builder.build();

  // Validation successful.
  return rowType;
}
 
Example 10
Source File: FunctionalIndexHelper.java    From Bats with Apache License 2.0 4 votes vote down vote up
/**
 * if a field in rowType serves only the to-be-replaced column(s), we should replace it with new name "$1",
 * otherwise we should keep this dataTypeField and add a new one for "$1"
 * @param origScan  the original scan whose rowtype is to be rewritten
 * @param indexContext the index plan context
 * @param functionInfo functional index information that may impact rewrite
 * @return
 */
public static RelDataType rewriteFunctionalRowType(RelNode origScan, IndexCallContext indexContext,
    FunctionalIndexInfo functionInfo, Collection<SchemaPath> addedPaths) {
  RelDataType origRowType = origScan.getRowType();
  if (!functionInfo.hasFunctional()) {
    return origRowType;
  }

  List<RelDataTypeField> fields = Lists.newArrayList();

  Set<String> leftOutFieldNames  = Sets.newHashSet();
  if (indexContext.getLeftOutPathsInFunctions() != null) {
    for (LogicalExpression expr : indexContext.getLeftOutPathsInFunctions()) {
      leftOutFieldNames.add(((SchemaPath) expr).getRootSegmentPath());
    }
  }

  Set<String> fieldInFunctions  = Sets.newHashSet();
  for (SchemaPath path: functionInfo.allPathsInFunction()) {
    fieldInFunctions.add(path.getRootSegmentPath());
  }

  RelDataTypeFactory typeFactory = origScan.getCluster().getTypeFactory();

  for ( RelDataTypeField field: origRowType.getFieldList()) {
    final String fieldName = field.getName();
    if (fieldInFunctions.contains(fieldName)) {
      if (!leftOutFieldNames.contains(fieldName)) {
        continue;
      }
    }

    fields.add(new RelDataTypeFieldImpl(
        SchemaPath.parseFromString(fieldName).getRootSegmentPath(), fields.size(),
        typeFactory.createSqlType(SqlTypeName.ANY)));
  }

  final Collection<SchemaPath> toAddToRowType = (addedPaths == null)? functionInfo.allNewSchemaPaths() : addedPaths;

  for (SchemaPath dollarPath : toAddToRowType) {
    fields.add(
        new RelDataTypeFieldImpl(dollarPath.getRootSegmentPath(), fields.size(),
            origScan.getCluster().getTypeFactory().createSqlType(SqlTypeName.ANY)));
  }

  return new RelRecordType(fields);
}
 
Example 11
Source File: OLAPProjectRel.java    From kylin-on-parquet-v2 with Apache License 2.0 4 votes vote down vote up
ColumnRowType buildColumnRowType() {
    List<TblColRef> columns = Lists.newArrayList();
    List<TupleExpression> sourceColumns = Lists.newArrayList();

    OLAPRel olapChild = (OLAPRel) getInput();
    ColumnRowType inputColumnRowType = olapChild.getColumnRowType();
    boolean ifVerify = !hasSubQuery() && !afterAggregate;
    TupleExpressionVisitor visitor = new TupleExpressionVisitor(inputColumnRowType, ifVerify);
    for (int i = 0; i < this.rewriteProjects.size(); i++) {
        RexNode rex = this.rewriteProjects.get(i);
        RelDataTypeField columnField = this.rowType.getFieldList().get(i);
        String fieldName = columnField.getName();

        TupleExpression tupleExpr = rex.accept(visitor);
        TblColRef column = translateRexNode(rex, inputColumnRowType, tupleExpr, fieldName);
        if (!this.rewriting && !this.afterAggregate && !isMerelyPermutation) {
            Set<TblColRef> srcCols = ExpressionColCollector.collectColumns(tupleExpr);
            // remove cols not belonging to context tables
            Iterator<TblColRef> srcColIter = srcCols.iterator();
            while (srcColIter.hasNext()) {
                if (!context.belongToContextTables(srcColIter.next())) {
                    srcColIter.remove();
                }
            }
            this.context.allColumns.addAll(srcCols);

            if (this.context.isDynamicColumnEnabled() && tupleExpr.ifForDynamicColumn()) {
                SqlTypeName fSqlType = columnField.getType().getSqlTypeName();
                String dataType = OLAPTable.DATATYPE_MAPPING.get(fSqlType);
                // upgrade data type for number columns
                if (DataType.isNumberFamily(dataType)) {
                    dataType = "decimal";
                }
                column.getColumnDesc().setDatatype(dataType);
                this.context.dynamicFields.put(column, columnField.getType());
            }
        } else {
            tupleExpr = new NoneTupleExpression();
        }

        columns.add(column);
        sourceColumns.add(tupleExpr);
    }
    return new ColumnRowType(columns, sourceColumns);
}
 
Example 12
Source File: GlobalDictionaryVisitor.java    From dremio-oss with Apache License 2.0 4 votes vote down vote up
private RelDataTypeField dictionaryEncodedField(RelDataTypeField field) {
  return new RelDataTypeFieldImpl(field.getName(), field.getIndex(), dictionaryDataType);
}
 
Example 13
Source File: GlobalDictionaryVisitor.java    From dremio-oss with Apache License 2.0 4 votes vote down vote up
private PrelWithDictionaryInfo visitParquetScanPrel(ParquetScanPrel parquetScanPrel, Void value) throws RuntimeException {
  final ReadDefinition readDefinition = parquetScanPrel.getTableMetadata().getReadDefinition();

  if (readDefinition == null || readDefinition.getExtendedProperty() == null) {
    return new PrelWithDictionaryInfo(parquetScanPrel);
  }

  // Make sure we don't apply global dictionary on columns that have conditions pushed into the scan
  final Set<String> columnsPushedToScan = new HashSet<>();
  if (parquetScanPrel.getFilter() != null) {
    Iterables.addAll(columnsPushedToScan,
        Iterables.transform(parquetScanPrel.getFilter().getConditions(), ParquetFilterCondition.EXTRACT_COLUMN_NAME));
  }

  final Map<String, String> dictionaryEncodedColumnsToDictionaryFilePath = Maps.newHashMap();
  long dictionaryVersion = -1;

  ParquetDatasetXAttr xAttr = null;

  boolean isIcebergDataset = isIcebergDataset(parquetScanPrel.getTableMetadata());
  // Dremio 5.0 release had ParquetDatasetXAttr in extended property
  boolean newIcebergDataset = false;
  if (isIcebergDataset) {
    try {
      IcebergDatasetXAttr icebergDatasetXAttr = LegacyProtobufSerializer.parseFrom(IcebergDatasetXAttr.PARSER,
        readDefinition.getExtendedProperty().asReadOnlyByteBuffer());
      xAttr = icebergDatasetXAttr.getParquetDatasetXAttr();
      newIcebergDataset = true;
    } catch (InvalidProtocolBufferException ignored) {
    }
  }

  if (!isIcebergDataset || !newIcebergDataset) {
    try {
      xAttr = LegacyProtobufSerializer.parseFrom(ParquetDatasetXAttr.PARSER,
        readDefinition.getExtendedProperty().asReadOnlyByteBuffer());
    } catch (InvalidProtocolBufferException e) {
      if (isIcebergDataset) {
        throw new RuntimeException("Could not deserialize Iceberg dataset info");
      } else {
        throw new RuntimeException("Could not deserialize parquet dataset info");
      }
    }
  }

  if (xAttr.hasDictionaryEncodedColumns()) {
    final DictionaryEncodedColumns dictionaryEncodedColumns = xAttr.getDictionaryEncodedColumns();
    dictionaryVersion = dictionaryEncodedColumns.getVersion();
    // Construct paths to dictionary files based on the version found in namespace. Do NOT look for files during planning.
    final Path dictionaryRootPath = Path.of(dictionaryEncodedColumns.getRootPath());
    for (String dictionaryEncodedColumn : dictionaryEncodedColumns.getColumnsList()) {
      if (!columnsPushedToScan.contains(dictionaryEncodedColumn)) {
        dictionaryEncodedColumnsToDictionaryFilePath.put(dictionaryEncodedColumn,
          GlobalDictionaryBuilder.dictionaryFilePath(dictionaryRootPath, dictionaryEncodedColumn).toString());
      }
    }
  }

  if (dictionaryEncodedColumnsToDictionaryFilePath.isEmpty()) {
    return new PrelWithDictionaryInfo(parquetScanPrel);
  }

  final StoragePluginId storagePluginId = parquetScanPrel.getPluginId();
  boolean encodedColumns = false;
  final List<RelDataTypeField> newFields = Lists.newArrayList();
  final GlobalDictionaryFieldInfo[] fieldInfos = new GlobalDictionaryFieldInfo[parquetScanPrel.getRowType().getFieldCount()];
  final List<GlobalDictionaryFieldInfo> globalDictionaryColumns = Lists.newArrayList();

  for (int i = 0; i < parquetScanPrel.getRowType().getFieldCount(); ++i) {
    final RelDataTypeField field = parquetScanPrel.getRowType().getFieldList().get(i);
    if (dictionaryEncodedColumnsToDictionaryFilePath.containsKey(field.getName())) {
      fieldInfos[i] = new GlobalDictionaryFieldInfo(
        dictionaryVersion,
        field.getName(),
        storagePluginId,
        CalciteArrowHelper.fromRelAndMinorType(field
          .getType(), TypeInferenceUtils.getMinorTypeFromCalciteType(field
          .getType())).getType(),
        dictionaryEncodedColumnsToDictionaryFilePath.get(field.getName()),
        new RelDataTypeFieldImpl(field.getName(), field.getIndex(), field.getType()));
      newFields.add(dictionaryEncodedField(field));
      globalDictionaryColumns.add(fieldInfos[i]);
      encodedColumns = true;
    } else {
      fieldInfos[i] = null;
      newFields.add(field);
    }
  }

  if (!encodedColumns) {
    return new PrelWithDictionaryInfo(parquetScanPrel);
  }

  final RelDataType newRelDataType = PrelWithDictionaryInfo.toRowDataType(newFields, parquetScanPrel.getCluster().getTypeFactory());
  final ParquetScanPrel newParquetScanPrel = parquetScanPrel.cloneWithGlobalDictionaryColumns(globalDictionaryColumns, newRelDataType);
  return new PrelWithDictionaryInfo(newParquetScanPrel, fieldInfos);
}
 
Example 14
Source File: OLAPProjectRel.java    From kylin with Apache License 2.0 4 votes vote down vote up
ColumnRowType buildColumnRowType() {
    List<TblColRef> columns = Lists.newArrayList();
    List<TupleExpression> sourceColumns = Lists.newArrayList();

    OLAPRel olapChild = (OLAPRel) getInput();
    ColumnRowType inputColumnRowType = olapChild.getColumnRowType();
    boolean ifVerify = !hasSubQuery() && !afterAggregate;
    TupleExpressionVisitor visitor = new TupleExpressionVisitor(inputColumnRowType, ifVerify);
    for (int i = 0; i < this.rewriteProjects.size(); i++) {
        RexNode rex = this.rewriteProjects.get(i);
        RelDataTypeField columnField = this.rowType.getFieldList().get(i);
        String fieldName = columnField.getName();

        TupleExpression tupleExpr = rex.accept(visitor);
        TblColRef column = translateRexNode(rex, inputColumnRowType, tupleExpr, fieldName);
        if (!this.rewriting && !this.afterAggregate && !isMerelyPermutation) {
            Set<TblColRef> srcCols = ExpressionColCollector.collectColumns(tupleExpr);
            // remove cols not belonging to context tables
            Iterator<TblColRef> srcColIter = srcCols.iterator();
            while (srcColIter.hasNext()) {
                if (!context.belongToContextTables(srcColIter.next())) {
                    srcColIter.remove();
                }
            }
            this.context.allColumns.addAll(srcCols);

            if (this.context.isDynamicColumnEnabled() && tupleExpr.ifForDynamicColumn()) {
                SqlTypeName fSqlType = columnField.getType().getSqlTypeName();
                String dataType = OLAPTable.DATATYPE_MAPPING.get(fSqlType);
                column.getColumnDesc().setDatatype(dataType);
                this.context.dynamicFields.put(column, columnField.getType());
            }
        } else {
            tupleExpr = new NoneTupleExpression();
        }

        columns.add(column);
        sourceColumns.add(tupleExpr);
    }
    return new ColumnRowType(columns, sourceColumns);
}
 
Example 15
Source File: IdentifierNamespace.java    From calcite with Apache License 2.0 4 votes vote down vote up
public RelDataType validateImpl(RelDataType targetRowType) {
  resolvedNamespace = Objects.requireNonNull(resolveImpl(id));
  if (resolvedNamespace instanceof TableNamespace) {
    SqlValidatorTable table = resolvedNamespace.getTable();
    if (validator.config().identifierExpansion()) {
      // TODO:  expand qualifiers for column references also
      List<String> qualifiedNames = table.getQualifiedName();
      if (qualifiedNames != null) {
        // Assign positions to the components of the fully-qualified
        // identifier, as best we can. We assume that qualification
        // adds names to the front, e.g. FOO.BAR becomes BAZ.FOO.BAR.
        List<SqlParserPos> poses =
            new ArrayList<>(
                Collections.nCopies(
                    qualifiedNames.size(), id.getParserPosition()));
        int offset = qualifiedNames.size() - id.names.size();

        // Test offset in case catalog supports fewer qualifiers than catalog
        // reader.
        if (offset >= 0) {
          for (int i = 0; i < id.names.size(); i++) {
            poses.set(i + offset, id.getComponentParserPosition(i));
          }
        }
        id.setNames(qualifiedNames, poses);
      }
    }
  }

  RelDataType rowType = resolvedNamespace.getRowType();

  if (extendList != null) {
    if (!(resolvedNamespace instanceof TableNamespace)) {
      throw new RuntimeException("cannot convert");
    }
    resolvedNamespace =
        ((TableNamespace) resolvedNamespace).extend(extendList);
    rowType = resolvedNamespace.getRowType();
  }

  // Build a list of monotonic expressions.
  final ImmutableList.Builder<Pair<SqlNode, SqlMonotonicity>> builder =
      ImmutableList.builder();
  List<RelDataTypeField> fields = rowType.getFieldList();
  for (RelDataTypeField field : fields) {
    final String fieldName = field.getName();
    final SqlMonotonicity monotonicity =
        resolvedNamespace.getMonotonicity(fieldName);
    if (monotonicity != SqlMonotonicity.NOT_MONOTONIC) {
      builder.add(
          Pair.of((SqlNode) new SqlIdentifier(fieldName, SqlParserPos.ZERO),
              monotonicity));
    }
  }
  monotonicExprs = builder.build();

  // Validation successful.
  return rowType;
}