blob: 9d8f3ef6d888311b695460ef5f3b0b8bb0ec939b [file] [log] [blame]
/*
* 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.adapter.java.JavaTypeFactory;
import org.apache.calcite.jdbc.CalciteSchema;
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.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.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.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.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.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 {
final CalciteSchema rootSchema;
final JavaTypeFactory typeFactory;
private final List<String> defaultSchema;
private final boolean elideRecord = true;
private final boolean caseSensitive;
public CalciteCatalogReader(
CalciteSchema rootSchema,
boolean caseSensitive,
List<String> defaultSchema,
JavaTypeFactory typeFactory) {
super();
assert rootSchema != defaultSchema;
this.rootSchema = rootSchema;
this.caseSensitive = caseSensitive;
this.defaultSchema = defaultSchema;
this.typeFactory = typeFactory;
}
public CalciteCatalogReader withSchemaPath(List<String> schemaPath) {
return new CalciteCatalogReader(rootSchema, caseSensitive, schemaPath,
typeFactory);
}
public RelOptTableImpl getTable(final List<String> names) {
// First look in the default schema, if any.
if (defaultSchema != null) {
RelOptTableImpl table = getTableFrom(names, defaultSchema);
if (table != null) {
return table;
}
}
// If not found, look in the root schema
return getTableFrom(names, ImmutableList.<String>of());
}
private RelOptTableImpl getTableFrom(List<String> names,
List<String> schemaNames) {
CalciteSchema schema =
getSchema(Iterables.concat(schemaNames, Util.skipLast(names)));
if (schema == null) {
return null;
}
final String name = Util.last(names);
CalciteSchema.TableEntry entry = schema.getTable(name, caseSensitive);
if (entry == null) {
entry = schema.getTableBasedOnNullaryFunction(name, caseSensitive);
}
if (entry != null) {
final Table table = entry.getTable();
final String name2 = entry.name;
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<? extends List<String>> schemaNameList;
if (names.size() > 1) {
// If name is qualified, ignore path.
schemaNameList = ImmutableList.of(ImmutableList.<String>of());
} else {
CalciteSchema schema = getSchema(defaultSchema);
if (schema == null) {
schemaNameList = ImmutableList.of();
} else {
schemaNameList = schema.getPath();
}
}
for (List<String> schemaNames : schemaNameList) {
CalciteSchema schema =
getSchema(Iterables.concat(schemaNames, Util.skipLast(names)));
if (schema != null) {
final String name = Util.last(names);
functions2.addAll(schema.getFunctions(name, true));
}
}
return functions2;
}
private CalciteSchema getSchema(Iterable<String> schemaNames) {
CalciteSchema schema = rootSchema;
for (String schemaName : schemaNames) {
schema = schema.getSubSchema(schemaName, caseSensitive);
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);
if (schema == null) {
return ImmutableList.of();
}
final List<SqlMoniker> result = new ArrayList<>();
final Map<String, CalciteSchema> schemaMap = schema.getSubSchemaMap();
for (String subSchema : schemaMap.keySet()) {
result.add(
new SqlMonikerImpl(schema.path(subSchema), SqlMonikerType.SCHEMA));
}
for (String table : schema.getTableNames()) {
result.add(
new SqlMonikerImpl(schema.path(table), SqlMonikerType.TABLE));
}
final NavigableSet<String> functions = schema.getFunctionNames();
for (String function : functions) { // views are here as well
result.add(
new SqlMonikerImpl(schema.path(function), SqlMonikerType.FUNCTION));
}
return result;
}
public List<String> getSchemaName() {
return defaultSchema;
}
public RelOptTableImpl getTableForMember(List<String> names) {
return getTable(names);
}
public RelDataTypeField field(RelDataType rowType, String alias) {
return SqlValidatorUtil.lookupField(caseSensitive, elideRecord, rowType,
alias);
}
public int fieldOrdinal(RelDataType rowType, String alias) {
RelDataTypeField field = field(rowType, alias);
return field != null ? field.getIndex() : -1;
}
public boolean matches(String string, String name) {
return Util.matches(caseSensitive, string, name);
}
public int match(List<String> strings, String name) {
return Util.findMatch(strings, name, caseSensitive);
}
public RelDataType createTypeFromProjection(final RelDataType type,
final List<String> columnNameList) {
return SqlValidatorUtil.createTypeFromProjection(type, columnNameList,
typeFactory, caseSensitive, elideRecord);
}
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 Predicate<Function>() {
public boolean apply(Function function) {
return function instanceof TableMacro
|| function instanceof TableFunction;
}
};
} else {
predicate = new Predicate<Function>() {
public boolean apply(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 Predicate<Integer>() {
public boolean apply(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 typeFactory.toSql(type);
}
public List<SqlOperator> getOperatorList() {
return null;
}
public RelDataTypeFactory getTypeFactory() {
return typeFactory;
}
public void registerRules(RelOptPlanner planner) throws Exception {
}
@Override public boolean isCaseSensitive() {
return caseSensitive;
}
}
// End CalciteCatalogReader.java