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.prepare; import org.apache.calcite.jdbc.CalciteSchema; import org.apache.calcite.jdbc.JavaTypeFactoryImpl; import org.apache.calcite.plan.RelOptPlanner; import org.apache.calcite.rel.type.RelDataType; import org.apache.calcite.rel.type.RelDataTypeFactory; import org.apache.calcite.rel.type.RelDataTypeFactoryImpl; import org.apache.calcite.rel.type.RelDataTypeField; import org.apache.calcite.runtime.PredicateImpl; import org.apache.calcite.schema.AggregateFunction; import org.apache.calcite.schema.Function; import org.apache.calcite.schema.FunctionParameter; import org.apache.calcite.schema.ScalarFunction; import org.apache.calcite.schema.Table; import org.apache.calcite.schema.TableFunction; import org.apache.calcite.schema.TableMacro; import org.apache.calcite.schema.Wrapper; import org.apache.calcite.sql.SqlFunctionCategory; import org.apache.calcite.sql.SqlIdentifier; import org.apache.calcite.sql.SqlOperator; import org.apache.calcite.sql.SqlOperatorBinding; import org.apache.calcite.sql.SqlSyntax; import org.apache.calcite.sql.type.FamilyOperandTypeChecker; import org.apache.calcite.sql.type.InferTypes; import org.apache.calcite.sql.type.OperandTypes; import org.apache.calcite.sql.type.ReturnTypes; import org.apache.calcite.sql.type.SqlReturnTypeInference; import org.apache.calcite.sql.type.SqlTypeFamily; import org.apache.calcite.sql.type.SqlTypeName; import org.apache.calcite.sql.validate.SqlMoniker; import org.apache.calcite.sql.validate.SqlMonikerImpl; import org.apache.calcite.sql.validate.SqlMonikerType; import org.apache.calcite.sql.validate.SqlNameMatcher; import org.apache.calcite.sql.validate.SqlNameMatchers; import org.apache.calcite.sql.validate.SqlUserDefinedAggFunction; import org.apache.calcite.sql.validate.SqlUserDefinedFunction; import org.apache.calcite.sql.validate.SqlUserDefinedTableFunction; import org.apache.calcite.sql.validate.SqlUserDefinedTableMacro; import org.apache.calcite.sql.validate.SqlValidatorUtil; import org.apache.calcite.util.Util; import com.google.common.base.Preconditions; import com.google.common.base.Predicate; import com.google.common.base.Predicates; import com.google.common.collect.Collections2; import com.google.common.collect.ImmutableList; import com.google.common.collect.Iterables; import com.google.common.collect.Lists; import java.util.ArrayList; import java.util.Collection; import java.util.LinkedHashSet; import java.util.List; import java.util.Map; import java.util.NavigableSet; /** * Implementation of {@link org.apache.calcite.prepare.Prepare.CatalogReader} * and also {@link org.apache.calcite.sql.SqlOperatorTable} based on tables and * functions defined schemas. */ public class CalciteCatalogReader implements Prepare.CatalogReader { protected final CalciteSchema rootSchema; protected final RelDataTypeFactory typeFactory; private final List<List<String>> schemaPaths; protected final SqlNameMatcher nameMatcher; public CalciteCatalogReader(CalciteSchema rootSchema, boolean caseSensitive, List<String> defaultSchema, RelDataTypeFactory typeFactory) { this(rootSchema, SqlNameMatchers.withCaseSensitive(caseSensitive), ImmutableList.of(Preconditions.checkNotNull(defaultSchema), ImmutableList.<String>of()), typeFactory); } protected CalciteCatalogReader(CalciteSchema rootSchema, SqlNameMatcher nameMatcher, List<List<String>> schemaPaths, RelDataTypeFactory typeFactory) { this.rootSchema = Preconditions.checkNotNull(rootSchema); this.nameMatcher = nameMatcher; this.schemaPaths = Util .immutableCopy(Util.isDistinct(schemaPaths) ? schemaPaths : new LinkedHashSet<>(schemaPaths)); this.typeFactory = typeFactory; } public CalciteCatalogReader withSchemaPath(List<String> schemaPath) { return new CalciteCatalogReader(rootSchema, nameMatcher, ImmutableList.of(schemaPath, ImmutableList.<String>of()), typeFactory); } public Prepare.PreparingTable getTable(final List<String> names) { // First look in the default schema, if any. // If not found, look in the root schema. for (List<String> schemaPath : schemaPaths) { Prepare.PreparingTable table = getTableFrom(names, schemaPath, nameMatcher); if (table != null) { return table; } } return null; } private Prepare.PreparingTable getTableFrom(List<String> names, List<String> schemaNames, SqlNameMatcher nameMatcher) { CalciteSchema schema = getSchema(Iterables.concat(schemaNames, Util.skipLast(names)), nameMatcher); if (schema == null) { return null; } final String name = Util.last(names); CalciteSchema.TableEntry entry = schema.getTable(name, nameMatcher.isCaseSensitive()); if (entry == null) { entry = schema.getTableBasedOnNullaryFunction(name, nameMatcher.isCaseSensitive()); } if (entry != null) { final Table table = entry.getTable(); final String name2 = entry.name; if (table instanceof Wrapper) { final Prepare.PreparingTable relOptTable = ((Wrapper) table).unwrap(Prepare.PreparingTable.class); if (relOptTable != null) { return relOptTable; } } return RelOptTableImpl.create(this, table.getRowType(typeFactory), schema.add(name2, table), null); } return null; } private Collection<Function> getFunctionsFrom(List<String> names) { final List<Function> functions2 = Lists.newArrayList(); final List<List<String>> schemaNameList = new ArrayList<>(); if (names.size() > 1) { // Name qualified: ignore path. But we do look in "/catalog" and "/", // the last 2 items in the path. if (schemaPaths.size() > 1) { schemaNameList.addAll(Util.skip(schemaPaths)); } else { schemaNameList.addAll(schemaPaths); } } else { for (List<String> schemaPath : schemaPaths) { CalciteSchema schema = getSchema(schemaPath, nameMatcher); if (schema != null) { schemaNameList.addAll(schema.getPath()); } } } for (List<String> schemaNames : schemaNameList) { CalciteSchema schema = getSchema(Iterables.concat(schemaNames, Util.skipLast(names)), nameMatcher); if (schema != null) { final String name = Util.last(names); functions2.addAll(schema.getFunctions(name, true)); } } return functions2; } private CalciteSchema getSchema(Iterable<String> schemaNames, SqlNameMatcher nameMatcher) { CalciteSchema schema = rootSchema; for (String schemaName : schemaNames) { if (schema == rootSchema && nameMatcher.matches(schemaName, schema.getName())) { continue; } schema = schema.getSubSchema(schemaName, nameMatcher.isCaseSensitive()); if (schema == null) { return null; } } return schema; } public RelDataType getNamedType(SqlIdentifier typeName) { return null; } public List<SqlMoniker> getAllSchemaObjectNames(List<String> names) { final CalciteSchema schema = getSchema(names, nameMatcher); if (schema == null) { return ImmutableList.of(); } final List<SqlMoniker> result = new ArrayList<>(); // Add root schema if not anonymous if (!schema.name.equals("")) { result.add(moniker(schema, null, SqlMonikerType.SCHEMA)); } final Map<String, CalciteSchema> schemaMap = schema.getSubSchemaMap(); for (String subSchema : schemaMap.keySet()) { result.add(moniker(schema, subSchema, SqlMonikerType.SCHEMA)); } for (String table : schema.getTableNames()) { result.add(moniker(schema, table, SqlMonikerType.TABLE)); } final NavigableSet<String> functions = schema.getFunctionNames(); for (String function : functions) { // views are here as well result.add(moniker(schema, function, SqlMonikerType.FUNCTION)); } return result; } private SqlMonikerImpl moniker(CalciteSchema schema, String name, SqlMonikerType type) { final List<String> path = schema.path(name); if (path.size() == 1 && !schema.root().name.equals("") && type == SqlMonikerType.SCHEMA) { type = SqlMonikerType.CATALOG; } return new SqlMonikerImpl(path, type); } public List<List<String>> getSchemaPaths() { return schemaPaths; } public Prepare.PreparingTable getTableForMember(List<String> names) { return getTable(names); } @SuppressWarnings("deprecation") public RelDataTypeField field(RelDataType rowType, String alias) { return nameMatcher.field(rowType, alias); } @SuppressWarnings("deprecation") public boolean matches(String string, String name) { return nameMatcher.matches(string, name); } public RelDataType createTypeFromProjection(final RelDataType type, final List<String> columnNameList) { return SqlValidatorUtil.createTypeFromProjection(type, columnNameList, typeFactory, nameMatcher.isCaseSensitive()); } public void lookupOperatorOverloads(final SqlIdentifier opName, SqlFunctionCategory category, SqlSyntax syntax, List<SqlOperator> operatorList) { if (syntax != SqlSyntax.FUNCTION) { return; } final Predicate<Function> predicate; if (category == null) { predicate = Predicates.alwaysTrue(); } else if (category.isTableFunction()) { predicate = new PredicateImpl<Function>() { public boolean test(Function function) { return function instanceof TableMacro || function instanceof TableFunction; } }; } else { predicate = new PredicateImpl<Function>() { public boolean test(Function function) { return !(function instanceof TableMacro || function instanceof TableFunction); } }; } final Collection<Function> functions = Collections2.filter(getFunctionsFrom(opName.names), predicate); if (functions.isEmpty()) { return; } operatorList.addAll( Collections2.transform(functions, new com.google.common.base.Function<Function, SqlOperator>() { public SqlOperator apply(Function function) { return toOp(opName, function); } })); } private SqlOperator toOp(SqlIdentifier name, final Function function) { List<RelDataType> argTypes = new ArrayList<>(); List<SqlTypeFamily> typeFamilies = new ArrayList<>(); for (FunctionParameter o : function.getParameters()) { final RelDataType type = o.getType(typeFactory); argTypes.add(type); typeFamilies.add(Util.first(type.getSqlTypeName().getFamily(), SqlTypeFamily.ANY)); } final Predicate<Integer> optional = new PredicateImpl<Integer>() { public boolean test(Integer input) { return function.getParameters().get(input).isOptional(); } }; final FamilyOperandTypeChecker typeChecker = OperandTypes.family(typeFamilies, optional); final List<RelDataType> paramTypes = toSql(argTypes); if (function instanceof ScalarFunction) { return new SqlUserDefinedFunction(name, infer((ScalarFunction) function), InferTypes.explicit(argTypes), typeChecker, paramTypes, function); } else if (function instanceof AggregateFunction) { return new SqlUserDefinedAggFunction(name, infer((AggregateFunction) function), InferTypes.explicit(argTypes), typeChecker, (AggregateFunction) function); } else if (function instanceof TableMacro) { return new SqlUserDefinedTableMacro(name, ReturnTypes.CURSOR, InferTypes.explicit(argTypes), typeChecker, paramTypes, (TableMacro) function); } else if (function instanceof TableFunction) { return new SqlUserDefinedTableFunction(name, ReturnTypes.CURSOR, InferTypes.explicit(argTypes), typeChecker, paramTypes, (TableFunction) function); } else { throw new AssertionError("unknown function type " + function); } } private SqlReturnTypeInference infer(final ScalarFunction function) { return new SqlReturnTypeInference() { public RelDataType inferReturnType(SqlOperatorBinding opBinding) { final RelDataType type = function.getReturnType(typeFactory); return toSql(type); } }; } private SqlReturnTypeInference infer(final AggregateFunction function) { return new SqlReturnTypeInference() { public RelDataType inferReturnType(SqlOperatorBinding opBinding) { final RelDataType type = function.getReturnType(typeFactory); return toSql(type); } }; } private List<RelDataType> toSql(List<RelDataType> types) { return Lists.transform(types, new com.google.common.base.Function<RelDataType, RelDataType>() { public RelDataType apply(RelDataType type) { return toSql(type); } }); } private RelDataType toSql(RelDataType type) { if (type instanceof RelDataTypeFactoryImpl.JavaType && ((RelDataTypeFactoryImpl.JavaType) type).getJavaClass() == Object.class) { return typeFactory.createTypeWithNullability(typeFactory.createSqlType(SqlTypeName.ANY), true); } return JavaTypeFactoryImpl.toSql(typeFactory, type); } public List<SqlOperator> getOperatorList() { return null; } public CalciteSchema getRootSchema() { return rootSchema; } public RelDataTypeFactory getTypeFactory() { return typeFactory; } public void registerRules(RelOptPlanner planner) throws Exception { } @SuppressWarnings("deprecation") @Override public boolean isCaseSensitive() { return nameMatcher.isCaseSensitive(); } public SqlNameMatcher nameMatcher() { return nameMatcher; } @Override public <C> C unwrap(Class<C> aClass) { if (aClass.isInstance(this)) { return aClass.cast(this); } return null; } } // End CalciteCatalogReader.java