/*
* 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.drill.exec.store;
import java.io.IOException;
import java.util.Collection;
import java.util.Collections;
import java.util.List;
import java.util.Set;
import org.apache.calcite.linq4j.tree.DefaultExpression;
import org.apache.calcite.linq4j.tree.Expression;
import org.apache.calcite.rel.type.RelDataTypeFactory;
import org.apache.calcite.schema.Function;
import org.apache.calcite.schema.Schema;
import org.apache.calcite.schema.SchemaPlus;
import org.apache.calcite.schema.Table;
import org.apache.commons.lang3.tuple.Pair;
import org.apache.drill.common.exceptions.UserException;
import org.apache.drill.exec.dotdrill.View;
import org.apache.drill.exec.planner.logical.CreateTableEntry;
import com.google.common.base.Joiner;
import com.google.common.collect.Lists;
public abstract class AbstractSchema implements Schema, SchemaPartitionExplorer, AutoCloseable {
static final org.slf4j.Logger logger = org.slf4j.LoggerFactory.getLogger(AbstractSchema.class);
protected final List<String> schemaPath;
protected final String name;
private static final Expression EXPRESSION = new DefaultExpression(Object.class);
public AbstractSchema(List<String> parentSchemaPath, String name) {
schemaPath = Lists.newArrayList();
schemaPath.addAll(parentSchemaPath);
schemaPath.add(name);
this.name = name;
}
@Override
public Iterable<String> getSubPartitions(String table,
List<String> partitionColumns,
List<String> partitionValues
) throws PartitionNotFoundException {
throw new UnsupportedOperationException(
String.format("Schema of type: %s " +
"does not support retrieving sub-partition information.",
this.getClass().getSimpleName()));
}
public String getName() {
return name;
}
public List<String> getSchemaPath() {
return schemaPath;
}
public String getFullSchemaName() {
return Joiner.on(".").join(schemaPath);
}
public abstract String getTypeName();
/**
* The schema can be a top level schema which doesn't have its own tables, but refers
* to one of the default sub schemas for table look up.
*
* Default implementation returns itself.
*
* Ex. "dfs" schema refers to the tables in "default" workspace when querying for
* tables in "dfs" schema.
*
* @return Return the default schema where tables are created or retrieved from.
*/
public Schema getDefaultSchema() {
return this;
}
/**
* Create a new view given definition.
* @param view View info including name, definition etc.
* @return Returns true if an existing view is replaced with the given view. False otherwise.
* @throws IOException
*/
public boolean createView(View view) throws IOException {
throw UserException.unsupportedError()
.message("Creating new view is not supported in schema [%s]", getSchemaPath())
.build(logger);
}
/**
* Drop the view with given name.
*
* @param viewName
* @throws IOException
*/
public void dropView(String viewName) throws IOException {
throw UserException.unsupportedError()
.message("Dropping a view is supported in schema [%s]", getSchemaPath())
.build(logger);
}
/**
* Creates table entry using table name, list of partition columns
* and storage strategy used to create table folder and files
*
* @param tableName : new table name.
* @param partitionColumns : list of partition columns. Empty list if there is no partition columns.
* @param storageStrategy : storage strategy used to create table folder and files
* @return create table entry
*/
public CreateTableEntry createNewTable(String tableName, List<String> partitionColumns, StorageStrategy storageStrategy) {
throw UserException.unsupportedError()
.message("Creating new tables is not supported in schema [%s]", getSchemaPath())
.build(logger);
}
/**
* Creates table entry using table name and list of partition columns if any.
* Table folder and files will be created using persistent storage strategy.
*
* @param tableName : new table name.
* @param partitionColumns : list of partition columns. Empty list if there is no partition columns.
* @return create table entry
*/
public CreateTableEntry createNewTable(String tableName, List<String> partitionColumns) {
return createNewTable(tableName, partitionColumns, StorageStrategy.DEFAULT);
}
/**
* Reports whether to show items from this schema in INFORMATION_SCHEMA
* tables.
* (Controls ... TODO: Doc.: Mention what this typically controls or
* affects.)
* <p>
* This base implementation returns {@code true}.
* </p>
*/
public boolean showInInformationSchema() {
return true;
}
@Override
public Collection<Function> getFunctions(String name) {
return Collections.emptyList();
}
@Override
public Set<String> getFunctionNames() {
return Collections.emptySet();
}
@Override
public Schema getSubSchema(String name) {
return null;
}
@Override
public Set<String> getSubSchemaNames() {
return Collections.emptySet();
}
@Override
public boolean isMutable() {
return false;
}
@Override
public Table getTable(String name){
return null;
}
@Override
public Set<String> getTableNames() {
return Collections.emptySet();
}
@Override
public Expression getExpression(SchemaPlus parentSchema, String name) {
return EXPRESSION;
}
@Override
public boolean contentsHaveChangedSince(long lastCheck, long now) {
return true;
}
@Override
public void close() throws Exception {
// no-op: default implementation for most implementations.
}
public void dropTable(String tableName) {
throw UserException.unsupportedError()
.message("Dropping tables is not supported in schema [%s]", getSchemaPath())
.build(logger);
}
/**
* Get the collection of {@link Table} tables specified in the tableNames with bulk-load (if the underlying storage
* plugin supports).
* It is not guaranteed that the retrieved tables would have RowType and Statistic being fully populated.
*
* Specifically, calling {@link Table#getRowType(RelDataTypeFactory)} or {@link Table#getStatistic()} might incur
* {@link UnsupportedOperationException} being thrown.
*
* @param tableNames the requested tables, specified by the table names
* @return the collection of requested tables
*/
public List<Pair<String, ? extends Table>> getTablesByNamesByBulkLoad(final List<String> tableNames, int bulkSize) {
return getTablesByNames(tableNames);
}
/**
* Get the collection of {@link Table} tables specified in the tableNames.
*
* @param tableNames the requested tables, specified by the table names
* @return the collection of requested tables
*/
public List<Pair<String, ? extends Table>> getTablesByNames(final List<String> tableNames) {
final List<Pair<String, ? extends Table>> tables = Lists.newArrayList();
for (String tableName : tableNames) {
final Table table = getTable(tableName);
if (table == null) {
// Schema may return NULL for table if the query user doesn't have permissions to load the table. Ignore such
// tables as INFO SCHEMA is about showing tables which the use has access to query.
continue;
}
tables.add(Pair.of(tableName, table));
}
return tables;
}
public List<Pair<String, Schema.TableType>> getTableNamesAndTypes(boolean bulkLoad, int bulkSize) {
final List<String> tableNames = Lists.newArrayList(getTableNames());
final List<Pair<String, Schema.TableType>> tableNamesAndTypes = Lists.newArrayList();
final List<Pair<String, ? extends Table>> tables;
if (bulkLoad) {
tables = getTablesByNamesByBulkLoad(tableNames, bulkSize);
} else {
tables = getTablesByNames(tableNames);
}
for (Pair<String, ? extends Table> table : tables) {
tableNamesAndTypes.add(Pair.of(table.getKey(), table.getValue().getJdbcTableType()));
}
return tableNamesAndTypes;
}
}