blob: 45e4efe8c15ef7b4f8b5b82aef19aa35cbe06883 [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.drill.exec.planner.sql.parser;
import org.apache.calcite.sql.SqlCall;
import org.apache.calcite.sql.SqlCharStringLiteral;
import org.apache.calcite.sql.SqlIdentifier;
import org.apache.calcite.sql.SqlKind;
import org.apache.calcite.sql.SqlLiteral;
import org.apache.calcite.sql.SqlNode;
import org.apache.calcite.sql.SqlNodeList;
import org.apache.calcite.sql.SqlOperator;
import org.apache.calcite.sql.SqlSpecialOperator;
import org.apache.calcite.sql.SqlWriter;
import org.apache.calcite.sql.parser.SqlParserPos;
import org.apache.calcite.sql.util.SqlBasicVisitor;
import org.apache.drill.common.util.DrillStringUtils;
import org.apache.drill.exec.planner.sql.SchemaUtilities;
import org.apache.drill.exec.planner.sql.handlers.AbstractSqlHandler;
import org.apache.drill.exec.planner.sql.handlers.SqlHandlerConfig;
import org.apache.drill.exec.planner.sql.handlers.SchemaHandler;
import org.apache.drill.exec.planner.sql.handlers.SqlHandlerUtil;
import java.util.Arrays;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
import java.util.stream.Collectors;
/**
* Parent class for CREATE, DROP, DESCRIBE, ALTER SCHEMA commands.
* Holds logic common command property: table, path.
*/
public abstract class SqlSchema extends DrillSqlCall {
protected final SqlIdentifier table;
protected final SqlNode path;
protected SqlSchema(SqlParserPos pos, SqlIdentifier table, SqlNode path) {
super(pos);
this.table = table;
this.path = path;
}
@Override
public void unparse(SqlWriter writer, int leftPrec, int rightPrec) {
if (table != null) {
writer.keyword("FOR TABLE");
table.unparse(writer, leftPrec, rightPrec);
}
if (path != null) {
writer.keyword("PATH");
path.unparse(writer, leftPrec, rightPrec);
}
}
public boolean hasTable() {
return table != null;
}
public SqlIdentifier getTable() {
return table;
}
public List<String> getSchemaPath() {
return hasTable() ? SchemaUtilities.getSchemaPath(table) : null;
}
public String getTableName() {
if (hasTable()) {
String tableName = table.isSimple() ? table.getSimple() : table.names.get(table.names.size() - 1);
return DrillStringUtils.removeLeadingSlash(tableName);
}
return null;
}
public String getPath() {
return path == null ? null : path.accept(LiteralVisitor.INSTANCE);
}
protected Map<String, String> getProperties(SqlNodeList properties) {
if (properties == null) {
return null;
}
// preserve properties order
Map<String, String> map = new LinkedHashMap<>();
for (int i = 1; i < properties.size(); i += 2) {
map.put(properties.get(i - 1).accept(LiteralVisitor.INSTANCE),
properties.get(i).accept(LiteralVisitor.INSTANCE));
}
return map;
}
/**
* Visits literal and returns bare value (i.e. single quotes).
*/
private static class LiteralVisitor extends SqlBasicVisitor<String> {
static final LiteralVisitor INSTANCE = new LiteralVisitor();
@Override
public String visit(SqlLiteral literal) {
return literal.toValue();
}
}
/**
* CREATE SCHEMA sql call.
*/
public static class Create extends SqlSchema {
private final SqlCharStringLiteral schema;
private final SqlNode load;
private final SqlNodeList properties;
private final SqlLiteral createType;
public static final SqlSpecialOperator OPERATOR = new SqlSpecialOperator("CREATE_SCHEMA", SqlKind.OTHER_DDL) {
@Override
public SqlCall createCall(SqlLiteral functionQualifier, SqlParserPos pos, SqlNode... operands) {
return new Create(pos, (SqlCharStringLiteral) operands[0], operands[1],
(SqlIdentifier) operands[2], operands[3], (SqlNodeList) operands[4], (SqlLiteral) operands[5]);
}
};
public Create(SqlParserPos pos,
SqlCharStringLiteral schema,
SqlNode load,
SqlIdentifier table,
SqlNode path,
SqlNodeList properties,
SqlLiteral createType) {
super(pos, table, path);
this.schema = schema;
this.load = load;
this.properties = properties;
this.createType = createType;
}
@Override
public SqlOperator getOperator() {
return OPERATOR;
}
@Override
public List<SqlNode> getOperandList() {
return Arrays.asList(schema, load, table, path, properties, createType);
}
@Override
public void unparse(SqlWriter writer, int leftPrec, int rightPrec) {
writer.keyword("CREATE");
if (SqlCreateType.OR_REPLACE == getSqlCreateType()) {
writer.keyword("OR");
writer.keyword("REPLACE");
}
if (schema != null) {
writer.keyword("SCHEMA");
writer.literal(getSchema());
}
if (load != null) {
writer.keyword("LOAD");
load.unparse(writer, leftPrec, rightPrec);
}
super.unparse(writer, leftPrec, rightPrec);
if (properties != null) {
writer.keyword("PROPERTIES");
SqlHandlerUtil.unparseKeyValuePairs(writer, leftPrec, rightPrec, properties);
}
}
@Override
public AbstractSqlHandler getSqlHandler(SqlHandlerConfig config) {
return new SchemaHandler.Create(config);
}
public boolean hasSchema() {
return schema != null;
}
public String getSchema() {
return hasSchema() ? schema.toValue() : null;
}
public String getLoad() {
return load == null ? null : load.accept(LiteralVisitor.INSTANCE);
}
public Map<String, String> getProperties() {
return getProperties(properties);
}
public SqlCreateType getSqlCreateType() {
return SqlCreateType.valueOf(createType.toValue());
}
}
/**
* DROP SCHEMA sql call.
*/
public static class Drop extends SqlSchema {
private final SqlLiteral existenceCheck;
public static final SqlSpecialOperator OPERATOR = new SqlSpecialOperator("DROP_SCHEMA", SqlKind.OTHER_DDL) {
@Override
public SqlCall createCall(SqlLiteral functionQualifier, SqlParserPos pos, SqlNode... operands) {
return new Drop(pos, (SqlIdentifier) operands[0], (SqlLiteral) operands[1]);
}
};
public Drop(SqlParserPos pos, SqlIdentifier table, SqlLiteral existenceCheck) {
super(pos, table, null);
this.existenceCheck = existenceCheck;
}
@Override
public SqlOperator getOperator() {
return OPERATOR;
}
@Override
public List<SqlNode> getOperandList() {
return Arrays.asList(table, existenceCheck);
}
@Override
public void unparse(SqlWriter writer, int leftPrec, int rightPrec) {
writer.keyword("DROP");
writer.keyword("SCHEMA");
if (ifExists()) {
writer.keyword("IF");
writer.keyword("EXISTS");
}
super.unparse(writer, leftPrec, rightPrec);
}
@Override
public AbstractSqlHandler getSqlHandler(SqlHandlerConfig config) {
return new SchemaHandler.Drop(config);
}
public boolean ifExists() {
return existenceCheck.booleanValue();
}
}
/**
* DESCRIBE SCHEMA FOR TABLE sql call.
*/
public static class Describe extends SqlSchema {
private final SqlLiteral format;
public static final SqlSpecialOperator OPERATOR = new SqlSpecialOperator(SqlKind.DESCRIBE_SCHEMA.name(), SqlKind.DESCRIBE_SCHEMA) {
@Override
public SqlCall createCall(SqlLiteral functionQualifier, SqlParserPos pos, SqlNode... operands) {
return new Describe(pos, (SqlIdentifier) operands[0], (SqlLiteral) operands[1]);
}
};
public Describe(SqlParserPos pos, SqlIdentifier table, SqlLiteral format) {
super(pos, table, null);
this.format = format;
}
@Override
public SqlOperator getOperator() {
return OPERATOR;
}
@Override
public List<SqlNode> getOperandList() {
return Arrays.asList(table, format);
}
@Override
public void unparse(SqlWriter writer, int leftPrec, int rightPrec) {
writer.keyword("DESCRIBE");
writer.keyword("SCHEMA");
super.unparse(writer, leftPrec, rightPrec);
writer.keyword("AS");
writer.keyword(getFormat().name());
}
public Describe.Format getFormat() {
return Format.valueOf(format.toValue());
}
/**
* Enum which specifies format of DESCRIBE SCHEMA FOR table output.
*/
public enum Format {
/**
* Schema will be output in JSON format used to store schema
* in {@link org.apache.drill.exec.record.metadata.schema.SchemaProvider#DEFAULT_SCHEMA_NAME} file.
*/
JSON,
/**
* Schema will be output in CREATE SCHEMA command syntax.
*/
STATEMENT
}
}
public static class Add extends SqlSchema {
private final SqlLiteral replace;
private final SqlCharStringLiteral schema;
private final SqlNodeList properties;
public static final SqlSpecialOperator OPERATOR = new SqlSpecialOperator("ALTER_SCHEMA_ADD", SqlKind.OTHER_DDL) {
@Override
public SqlCall createCall(SqlLiteral functionQualifier, SqlParserPos pos, SqlNode... operands) {
return new Add(pos, (SqlIdentifier) operands[0], operands[1], (SqlLiteral) operands[2],
(SqlCharStringLiteral) operands[3], (SqlNodeList) operands[4]);
}
};
public Add(SqlParserPos pos,
SqlIdentifier table,
SqlNode path,
SqlLiteral replace,
SqlCharStringLiteral schema,
SqlNodeList properties) {
super(pos, table, path);
this.replace = replace;
this.schema = schema;
this.properties = properties;
}
@Override
public SqlOperator getOperator() {
return OPERATOR;
}
@Override
public List<SqlNode> getOperandList() {
return Arrays.asList(table, path, replace, schema, properties);
}
@Override
public void unparse(SqlWriter writer, int leftPrec, int rightPrec) {
writer.keyword("ALTER");
writer.keyword("SCHEMA");
writer.keyword("ADD");
if (replace.booleanValue()) {
writer.keyword("OR");
writer.keyword("REPLACE");
}
super.unparse(writer, leftPrec, rightPrec);
if (schema != null) {
writer.keyword("COLUMNS");
writer.literal(getSchema());
}
if (properties != null) {
writer.keyword("PROPERTIES");
SqlHandlerUtil.unparseKeyValuePairs(writer, leftPrec, rightPrec, properties);
}
}
@Override
public AbstractSqlHandler getSqlHandler(SqlHandlerConfig config) {
return new SchemaHandler.Add(config);
}
public boolean isReplace() {
return replace.booleanValue();
}
public boolean hasSchema() {
return schema != null;
}
public String getSchema() {
return hasSchema() ? schema.toValue() : null;
}
public boolean hasProperties() {
return properties != null;
}
public Map<String, String> getProperties() {
return getProperties(properties);
}
}
public static class Remove extends SqlSchema {
private final SqlNodeList columns;
private final SqlNodeList properties;
public static final SqlSpecialOperator OPERATOR = new SqlSpecialOperator("ALTER_SCHEMA_REMOVE", SqlKind.OTHER_DDL) {
@Override
public SqlCall createCall(SqlLiteral functionQualifier, SqlParserPos pos, SqlNode... operands) {
return new Remove(pos, (SqlIdentifier) operands[0], operands[1],
(SqlNodeList) operands[2], (SqlNodeList) operands[3]);
}
};
public Remove(SqlParserPos pos,
SqlIdentifier table,
SqlNode path,
SqlNodeList columns,
SqlNodeList properties) {
super(pos, table, path);
this.columns = columns;
this.properties = properties;
}
@Override
public SqlOperator getOperator() {
return OPERATOR;
}
@Override
public List<SqlNode> getOperandList() {
return Arrays.asList(table, path, columns, properties);
}
@Override
public void unparse(SqlWriter writer, int leftPrec, int rightPrec) {
writer.keyword("ALTER");
writer.keyword("SCHEMA");
writer.keyword("REMOVE");
super.unparse(writer, leftPrec, rightPrec);
if (columns != null) {
writer.keyword("COLUMNS");
SqlHandlerUtil.unparseSqlNodeList(writer, leftPrec, rightPrec, columns);
}
if (properties != null) {
writer.keyword("PROPERTIES");
SqlHandlerUtil.unparseSqlNodeList(writer, leftPrec, rightPrec, properties);
}
}
@Override
public AbstractSqlHandler getSqlHandler(SqlHandlerConfig config) {
return new SchemaHandler.Remove(config);
}
public List<String> getColumns() {
if (columns == null) {
return null;
}
return columns.getList().stream()
.map(SqlNode::toString)
.collect(Collectors.toList());
}
public boolean hasProperties() {
return properties != null;
}
public List<String> getProperties() {
if (properties == null) {
return null;
}
return properties.getList().stream()
.map(property -> property.accept(LiteralVisitor.INSTANCE))
.collect(Collectors.toList());
}
}
}