SimpleCalciteSchema.java

/*
 * 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.jdbc;

import org.apache.calcite.rel.type.RelProtoDataType;
import org.apache.calcite.schema.Function;
import org.apache.calcite.schema.Schema;
import org.apache.calcite.schema.SchemaVersion;
import org.apache.calcite.schema.Table;
import org.apache.calcite.schema.TableMacro;
import org.apache.calcite.util.NameMap;
import org.apache.calcite.util.NameMultimap;
import org.apache.calcite.util.NameSet;

import com.google.common.collect.ImmutableList;
import com.google.common.collect.ImmutableSortedMap;
import com.google.common.collect.ImmutableSortedSet;

import org.checkerframework.checker.nullness.qual.Nullable;

import java.util.Collection;
import java.util.List;
import java.util.Locale;
import java.util.Set;

/**
 * A concrete implementation of {@link org.apache.calcite.jdbc.CalciteSchema}
 * that maintains minimal state.
 */
class SimpleCalciteSchema extends CalciteSchema {
  /** Creates a SimpleCalciteSchema.
   *
   * <p>Use {@link CalciteSchema#createRootSchema(boolean)}
   * or {@link #add(String, Schema)}. */
  SimpleCalciteSchema(@Nullable CalciteSchema parent, Schema schema, String name) {
    this(parent, schema, name, null, null, null, null, null, null, null, null);
  }

  private SimpleCalciteSchema(@Nullable CalciteSchema parent,
      Schema schema,
      String name,
      @Nullable NameMap<CalciteSchema> subSchemaMap,
      @Nullable NameMap<TableEntry> tableMap,
      @Nullable NameMap<LatticeEntry> latticeMap,
      @Nullable NameMap<TypeEntry> typeMap,
      @Nullable NameMultimap<FunctionEntry> functionMap,
      @Nullable NameSet functionNames,
      @Nullable NameMap<FunctionEntry> nullaryFunctionMap,
      @Nullable List<? extends List<String>> path) {
    super(parent, schema, name, subSchemaMap, tableMap, latticeMap, typeMap,
        functionMap, functionNames, nullaryFunctionMap, path);
  }

  @Override public void setCache(boolean cache) {
    throw new UnsupportedOperationException();
  }

  @Override public CalciteSchema add(String name, Schema schema) {
    final CalciteSchema calciteSchema =
        new SimpleCalciteSchema(this, schema, name);
    subSchemaMap.put(name, calciteSchema);
    return calciteSchema;
  }

  private static @Nullable String caseInsensitiveLookup(Set<String> candidates, String name) {
    // Exact string lookup
    if (candidates.contains(name)) {
      return name;
    }
    // Upper case string lookup
    final String upperCaseName = name.toUpperCase(Locale.ROOT);
    if (candidates.contains(upperCaseName)) {
      return upperCaseName;
    }
    // Lower case string lookup
    final String lowerCaseName = name.toLowerCase(Locale.ROOT);
    if (candidates.contains(lowerCaseName)) {
      return lowerCaseName;
    }
    // Fall through: Set iteration
    for (String candidate : candidates) {
      if (candidate.equalsIgnoreCase(name)) {
        return candidate;
      }
    }
    return null;
  }

  @Override protected CalciteSchema createSubSchema(Schema schema, String name) {
    return new SimpleCalciteSchema(this, schema, name);
  }

  @Override protected @Nullable TypeEntry getImplicitType(String name, boolean caseSensitive) {
    // Check implicit types.
    final String name2 =
        caseSensitive ? name
            : caseInsensitiveLookup(schema.getTypeNames(), name);
    if (name2 == null) {
      return null;
    }
    final RelProtoDataType type = schema.getType(name2);
    if (type == null) {
      return null;
    }
    return typeEntry(name2, type);
  }

  @Override protected void addImplicitFunctionsToBuilder(
      ImmutableList.Builder<Function> builder,
      String name, boolean caseSensitive) {
    Collection<Function> functions = schema.getFunctions(name);
    if (functions != null) {
      builder.addAll(functions);
    }
  }

  @Override protected void addImplicitFuncNamesToBuilder(
      ImmutableSortedSet.Builder<String> builder) {
    builder.addAll(schema.getFunctionNames());
  }

  @Override protected void addImplicitTypeNamesToBuilder(
      ImmutableSortedSet.Builder<String> builder) {
    builder.addAll(schema.getTypeNames());
  }

  @Override protected void addImplicitTablesBasedOnNullaryFunctionsToBuilder(
      ImmutableSortedMap.Builder<String, Table> builder) {
    ImmutableSortedMap<String, Table> explicitTables = builder.build();

    for (String s : schema.getFunctionNames()) {
      // explicit table wins.
      if (explicitTables.containsKey(s)) {
        continue;
      }
      for (Function function : schema.getFunctions(s)) {
        if (function instanceof TableMacro
            && function.getParameters().isEmpty()) {
          final Table table = ((TableMacro) function).apply(ImmutableList.of());
          builder.put(s, table);
        }
      }
    }
  }

  @Override protected @Nullable TableEntry getImplicitTableBasedOnNullaryFunction(String tableName,
      boolean caseSensitive) {
    Collection<Function> functions = schema.getFunctions(tableName);
    if (functions != null) {
      for (Function function : functions) {
        if (function instanceof TableMacro
            && function.getParameters().isEmpty()) {
          final Table table = ((TableMacro) function).apply(ImmutableList.of());
          return tableEntry(tableName, table);
        }
      }
    }
    return null;
  }

  @Override protected CalciteSchema snapshot(@Nullable CalciteSchema parent,
      SchemaVersion version) {
    CalciteSchema snapshot =
        new SimpleCalciteSchema(parent, schema.snapshot(version), name, null,
            tableMap, latticeMap, typeMap,
            functionMap, functionNames, nullaryFunctionMap, getPath());
    for (CalciteSchema subSchema : subSchemaMap.map().values()) {
      CalciteSchema subSchemaSnapshot = subSchema.snapshot(snapshot, version);
      snapshot.subSchemaMap.put(subSchema.name, subSchemaSnapshot);
    }
    return snapshot;
  }

  @Override protected boolean isCacheEnabled() {
    return false;
  }

}