org.apache.drill.exec.store.AbstractSchema Maven / Gradle / Ivy
/*
* 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.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.List;
import java.util.Map;
import java.util.Objects;
import java.util.Set;
import java.util.stream.Collectors;
import java.util.stream.IntStream;
import org.apache.drill.common.exceptions.DrillRuntimeException;
import org.apache.drill.exec.planner.logical.DrillTable;
import org.apache.drill.exec.planner.logical.ModifyTableEntry;
import org.apache.drill.exec.record.metadata.schema.SchemaProvider;
import org.apache.drill.exec.record.metadata.schema.SchemaProviderFactory;
import org.apache.drill.exec.store.table.function.TableParamDef;
import org.apache.drill.exec.store.table.function.TableSignature;
import org.apache.drill.exec.store.table.function.WithOptionsTableMacro;
import org.apache.drill.shaded.guava.com.google.common.collect.ImmutableMap;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.apache.calcite.linq4j.tree.DefaultExpression;
import org.apache.calcite.linq4j.tree.Expression;
import org.apache.calcite.rel.type.RelProtoDataType;
import org.apache.calcite.schema.Function;
import org.apache.calcite.schema.Schema;
import org.apache.calcite.schema.SchemaPlus;
import org.apache.calcite.schema.SchemaVersion;
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 org.apache.drill.shaded.guava.com.google.common.base.Joiner;
public abstract class AbstractSchema implements Schema, SchemaPartitionExplorer, AutoCloseable {
static final Logger logger = LoggerFactory.getLogger(AbstractSchema.class);
private static final Expression EXPRESSION = new DefaultExpression(Object.class);
private static final String SCHEMA_PARAMETER_NAME = "schema";
/**
* Schema parameter for table function which creates schema provider based on
* given parameter value.
*/
private static final TableParamDef SCHEMA_PARAMETER = TableParamDef.optional(
SCHEMA_PARAMETER_NAME, String.class, (drillTable, value) -> {
if (value == null) {
return;
}
SchemaProvider schemaProvider;
try {
schemaProvider = SchemaProviderFactory.create(String.valueOf(value));
// since we path schema here as table parameter
// read schema to ensure that schema is valid before query execution
schemaProvider.read();
} catch (IOException | IllegalArgumentException e) {
throw UserException.validationError(e)
.message(e.getMessage())
.addContext("Schema parameter value [%s]", value)
.build(logger);
}
drillTable.getMetadataProviderManager().setSchemaProvider(schemaProvider);
});
public static final String SCHEMA_SEPARATOR = ".";
protected final List schemaPath;
protected final String name;
public AbstractSchema(List parentSchemaPath, String name) {
name = name == null ? null : name.toLowerCase();
schemaPath = new ArrayList<>();
schemaPath.addAll(parentSchemaPath);
schemaPath.add(name);
this.name = name;
}
@Override
public Iterable getSubPartitions(String table,
List partitionColumns,
List 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 getSchemaPath() {
return schemaPath;
}
public String getFullSchemaName() {
return Joiner.on(SCHEMA_SEPARATOR).join(schemaPath);
}
@Override
public final String toString() {
return getFullSchemaName();
}
/**
* Returns string describing schema type which shows where the schema came from.
* Good practice here is to return json type name of storage plugin's config.
*
* @return schema type name
*/
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 in case of error creating a view
*/
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 view name
* @throws IOException in case of error dropping the view
*/
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 partitionColumns, StorageStrategy storageStrategy) {
throw UserException.unsupportedError()
.message("Creating new tables is not supported in schema [%s]", getSchemaPath())
.build(logger);
}
/**
* Returns table entry using table name to insert records into the table.
*
* @param tableName : new table name.
* @return insert table entry
*/
public ModifyTableEntry modifyTable(String tableName) {
throw UserException.unsupportedError()
.message("Modifying 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 partitionColumns) {
return createNewTable(tableName, partitionColumns, StorageStrategy.DEFAULT);
}
/**
* Create stats table entry for given tableName.
* @param tableName table name
* @return instance of create table entry
*/
public CreateTableEntry createStatsTable(String tableName) {
throw UserException.unsupportedError()
.message("Statistics tables are not supported in schema [%s]", getSchemaPath())
.build(logger);
}
/**
* Create an append statistics table entry for given tableName. If there is not existing
* statistics table, a new one is created.
* @param tableName table name
* @return instance of create table entry
*/
public CreateTableEntry appendToStatsTable(String tableName) {
throw UserException.unsupportedError()
.message("Statistics tables are not supported in schema [%s]", getSchemaPath())
.build(logger);
}
/**
* Get the statistics table for given tableName
* @param tableName table name
* @return instance of statistics table
*/
public Table getStatsTable(String tableName) {
throw UserException.unsupportedError()
.message("Statistics tables are not supported in schema [%s]", getSchemaPath())
.build(logger);
}
/**
* Reports whether to show items from this schema in INFORMATION_SCHEMA
* tables.
* (Controls ... TODO: Doc.: Mention what this typically controls or
* affects.)
*
* This base implementation returns {@code true}.
*
*/
public boolean showInInformationSchema() {
return true;
}
/**
* For the given table names returns list of acceptable table functions
* which are common for all Drill schemas. When overriding this method,
* parent functions must be included first to be evaluated first.
* If not included, parent functions won't be taken into account when creating table instance.
*
* @param name table name
* @return list of table functions
*/
@Override
public Collection getFunctions(String name) {
List parameters = getFunctionParameters();
TableSignature signature = TableSignature.of(name, parameters);
WithOptionsTableMacro function = new WithOptionsTableMacro(signature, arguments -> {
Table table = getTable(name);
if (table instanceof DrillTable) {
return applyFunctionParameters((DrillTable) table, parameters, arguments);
} else if (table == null) {
return null;
}
throw new DrillRuntimeException(String.format("Table [%s] is not of Drill table instance. " +
"Given instance is of [%s].", name, table.getClass().getName()));
});
return Collections.singletonList(function);
}
/**
* Returns of common table function parameters that can be used all Drill schema implementations.
*
* @return list of table function parameters
*/
public List getFunctionParameters() {
return Collections.singletonList(SCHEMA_PARAMETER);
}
/**
* For the given list of parameters definitions executes action for the corresponding value.
*
* @param drillTable Drill table instance
* @param paramDefs parameter definitions
* @param values parameter values
* @return updated Drill table instance
*/
public DrillTable applyFunctionParameters(DrillTable drillTable, List paramDefs, List