Java tutorial
/* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to you under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.calcite.schema.impl; import org.apache.calcite.plan.RelOptTable; import org.apache.calcite.plan.RelOptUtil; import org.apache.calcite.rel.type.RelDataType; import org.apache.calcite.rel.type.RelDataTypeFactory; import org.apache.calcite.rel.type.RelDataTypeField; import org.apache.calcite.rel.type.RelDataTypeImpl; import org.apache.calcite.rel.type.RelProtoDataType; import org.apache.calcite.rex.RexBuilder; import org.apache.calcite.rex.RexNode; import org.apache.calcite.schema.ExtensibleTable; import org.apache.calcite.schema.ModifiableView; import org.apache.calcite.schema.Path; import org.apache.calcite.schema.Table; import org.apache.calcite.schema.Wrapper; import org.apache.calcite.sql.SqlFunction; import org.apache.calcite.sql2rel.InitializerContext; import org.apache.calcite.sql2rel.InitializerExpressionFactory; import org.apache.calcite.sql2rel.NullInitializerExpressionFactory; import org.apache.calcite.util.ImmutableIntList; import com.google.common.collect.ImmutableList; import com.google.common.collect.ImmutableMap; import com.google.common.collect.Maps; import java.lang.reflect.Type; import java.util.ArrayList; import java.util.List; import java.util.Map; import static org.apache.calcite.sql.validate.SqlValidatorUtil.mapNameToIndex; /** Extension to {@link ViewTable} that is modifiable. */ public class ModifiableViewTable extends ViewTable implements ModifiableView, Wrapper { private final Table table; private final Path tablePath; private final RexNode constraint; private final ImmutableIntList columnMapping; private final InitializerExpressionFactory initializerExpressionFactory; /** Creates a ModifiableViewTable. */ public ModifiableViewTable(Type elementType, RelProtoDataType rowType, String viewSql, List<String> schemaPath, List<String> viewPath, Table table, Path tablePath, RexNode constraint, ImmutableIntList columnMapping) { super(elementType, rowType, viewSql, schemaPath, viewPath); this.table = table; this.tablePath = tablePath; this.constraint = constraint; this.columnMapping = columnMapping; this.initializerExpressionFactory = new ModifiableViewTableInitializerExpressionFactory(); } public RexNode getConstraint(RexBuilder rexBuilder, RelDataType tableRowType) { return rexBuilder.copy(constraint); } public ImmutableIntList getColumnMapping() { return columnMapping; } public Table getTable() { return table; } public Path getTablePath() { return tablePath; } @Override public <C> C unwrap(Class<C> aClass) { if (aClass.isInstance(initializerExpressionFactory)) { return aClass.cast(initializerExpressionFactory); } else if (aClass.isInstance(table)) { return aClass.cast(table); } return null; } /** * Extends the underlying table and returns a new view with updated row-type * and column-mapping. * * <p>The type factory is used to perform some scratch calculations, viz the * type mapping, but the "real" row-type will be assigned later, when the * table has been bound to the statement's type factory. The is important, * because adding types to type factories that do not belong to a statement * could potentially leak memory. * * @param extendedColumns Extended fields * @param typeFactory Type factory */ public final ModifiableViewTable extend(List<RelDataTypeField> extendedColumns, RelDataTypeFactory typeFactory) { final ExtensibleTable underlying = unwrap(ExtensibleTable.class); assert underlying != null; final RelDataTypeFactory.FieldInfoBuilder builder = typeFactory.builder(); final RelDataType rowType = getRowType(typeFactory); for (RelDataTypeField column : rowType.getFieldList()) { builder.add(column); } for (RelDataTypeField column : extendedColumns) { builder.add(column); } // The characteristics of the new view. final RelDataType newRowType = builder.build(); final ImmutableIntList newColumnMapping = getNewColumnMapping(underlying, getColumnMapping(), extendedColumns, typeFactory); // Extend the underlying table with only the fields that // duplicate column names in neither the view nor the base table. final List<RelDataTypeField> underlyingColumns = underlying.getRowType(typeFactory).getFieldList(); final List<RelDataTypeField> columnsOfExtendedBaseTable = RelOptUtil.deduplicateColumns(underlyingColumns, extendedColumns); final List<RelDataTypeField> extendColumnsOfBaseTable = columnsOfExtendedBaseTable .subList(underlyingColumns.size(), columnsOfExtendedBaseTable.size()); final Table extendedTable = underlying.extend(extendColumnsOfBaseTable); return extend(extendedTable, RelDataTypeImpl.proto(newRowType), newColumnMapping); } /** * Creates a mapping from the view index to the index in the underlying table. */ private static ImmutableIntList getNewColumnMapping(Table underlying, ImmutableIntList oldColumnMapping, List<RelDataTypeField> extendedColumns, RelDataTypeFactory typeFactory) { final List<RelDataTypeField> baseColumns = underlying.getRowType(typeFactory).getFieldList(); final Map<String, Integer> nameToIndex = mapNameToIndex(baseColumns); final ImmutableList.Builder<Integer> newMapping = ImmutableList.builder(); newMapping.addAll(oldColumnMapping); int newMappedIndex = baseColumns.size(); for (RelDataTypeField extendedColumn : extendedColumns) { if (nameToIndex.containsKey(extendedColumn.getName())) { // The extended column duplicates a column in the underlying table. // Map to the index in the underlying table. newMapping.add(nameToIndex.get(extendedColumn.getName())); } else { // The extended column is not in the underlying table. newMapping.add(newMappedIndex++); } } return ImmutableIntList.copyOf(newMapping.build()); } protected ModifiableViewTable extend(Table extendedTable, RelProtoDataType protoRowType, ImmutableIntList newColumnMapping) { return new ModifiableViewTable(getElementType(), protoRowType, getViewSql(), getSchemaPath(), getViewPath(), extendedTable, getTablePath(), constraint, newColumnMapping); } /** * Initializes columns based on the view constraint. */ private class ModifiableViewTableInitializerExpressionFactory extends NullInitializerExpressionFactory { private final ImmutableMap<Integer, RexNode> projectMap; private ModifiableViewTableInitializerExpressionFactory() { super(); final Map<Integer, RexNode> projectMap = Maps.newHashMap(); final List<RexNode> filters = new ArrayList<>(); RelOptUtil.inferViewPredicates(projectMap, filters, constraint); assert filters.isEmpty(); this.projectMap = ImmutableMap.copyOf(projectMap); } @Override public boolean isGeneratedAlways(RelOptTable table, int iColumn) { assert table.unwrap(ModifiableViewTable.class) != null; return false; } @Override public RexNode newColumnDefaultValue(RelOptTable table, int iColumn, InitializerContext context) { final ModifiableViewTable viewTable = table.unwrap(ModifiableViewTable.class); assert iColumn < viewTable.columnMapping.size(); final RexBuilder rexBuilder = context.getRexBuilder(); final RelDataTypeFactory typeFactory = rexBuilder.getTypeFactory(); final RelDataType viewType = viewTable.getRowType(typeFactory); final RelDataType iType = viewType.getFieldList().get(iColumn).getType(); // Use the view constraint to generate the default value if the column is constrained. final int mappedOrdinal = viewTable.columnMapping.get(iColumn); final RexNode viewConstraint = projectMap.get(mappedOrdinal); if (viewConstraint != null) { return rexBuilder.ensureType(iType, viewConstraint, true); } // Otherwise use the default value of the underlying table. final Table schemaTable = viewTable.unwrap(Table.class); if (schemaTable instanceof Wrapper) { final InitializerExpressionFactory initializerExpressionFactory = ((Wrapper) schemaTable) .unwrap(InitializerExpressionFactory.class); if (initializerExpressionFactory != null) { final RexNode tableConstraint = initializerExpressionFactory.newColumnDefaultValue(table, iColumn, context); return rexBuilder.ensureType(iType, tableConstraint, true); } } // Otherwise Sql type of NULL. return super.newColumnDefaultValue(table, iColumn, context); } @Override public RexNode newAttributeInitializer(RelDataType type, SqlFunction constructor, int iAttribute, List<RexNode> constructorArgs, InitializerContext context) { throw new UnsupportedOperationException("Not implemented - unknown requirements"); } } } // End ModifiableViewTable.java