diff --git a/sabot/kernel/src/main/codegen/data/Parser.tdd b/sabot/kernel/src/main/codegen/data/Parser.tdd index c18abb9d0a..0726d33c30 100644 --- a/sabot/kernel/src/main/codegen/data/Parser.tdd +++ b/sabot/kernel/src/main/codegen/data/Parser.tdd @@ -73,7 +73,8 @@ "FORCE", "LAZY", "MAINTAIN", - "MISSING" + "MISSING", + "FOLDER" ] nonReservedKeywords: [ @@ -111,7 +112,8 @@ "FORCE", "LAZY", "MAINTAIN", - "MISSING" + "MISSING", + "FOLDER" ] # List of methods for parsing custom SQL statements. @@ -128,7 +130,8 @@ "SqlAccel()", "SqlRefreshReflection()", "SqlLoadMaterialization()", - "SqlCompactMaterialization()" + "SqlCompactMaterialization()", + "SqlCreateFolderRecursive()" ] # List of methods for parsing custom literals. diff --git a/sabot/kernel/src/main/codegen/includes/parserImpls.ftl b/sabot/kernel/src/main/codegen/includes/parserImpls.ftl index 227f7efe15..f9882791d6 100644 --- a/sabot/kernel/src/main/codegen/includes/parserImpls.ftl +++ b/sabot/kernel/src/main/codegen/includes/parserImpls.ftl @@ -376,3 +376,21 @@ SqlNode SqlCompactMaterialization() : return new SqlCompactMaterialization(pos, materializationPath, newMaterializationId); } } + +/** + * Parses a CREATE FOLDER statement + */ +SqlNode SqlCreateFolderRecursive() : +{ + SqlParserPos pos; + SqlIdentifier folderPath; +} +{ + { pos = getPos(); } + + { folderPath = CompoundIdentifier(); } + + { + return new SqlCreateFolderRecursive(pos, folderPath); + } +} diff --git a/sabot/kernel/src/main/java/com/dremio/exec/planner/sql/handlers/commands/CommandCreator.java b/sabot/kernel/src/main/java/com/dremio/exec/planner/sql/handlers/commands/CommandCreator.java index 1ec399f0cd..46bb6a088b 100644 --- a/sabot/kernel/src/main/java/com/dremio/exec/planner/sql/handlers/commands/CommandCreator.java +++ b/sabot/kernel/src/main/java/com/dremio/exec/planner/sql/handlers/commands/CommandCreator.java @@ -21,6 +21,8 @@ import java.util.Locale; import java.util.Optional; +import com.dremio.exec.planner.sql.handlers.direct.CreateFolderRecursiveHandler; +import com.dremio.exec.planner.sql.parser.SqlCreateFolderRecursive; import org.apache.calcite.sql.SqlNode; import com.dremio.common.exceptions.UserException; @@ -313,6 +315,8 @@ private CommandRunner getSqlCommand(String sql, boolean isPrepare) { return direct.create(new RefreshSourceStatusHandler(catalog)); } else if (sqlNode instanceof SqlSetApprox) { return direct.create(new SetApproxHandler(catalog, context.getNamespaceService())); + } else if (sqlNode instanceof SqlCreateFolderRecursive) { + return direct.create(new CreateFolderRecursiveHandler(catalog, context.getNamespaceService())); } // fallthrough diff --git a/sabot/kernel/src/main/java/com/dremio/exec/planner/sql/handlers/direct/CreateFolderRecursiveHandler.java b/sabot/kernel/src/main/java/com/dremio/exec/planner/sql/handlers/direct/CreateFolderRecursiveHandler.java new file mode 100644 index 0000000000..dd9a7b031f --- /dev/null +++ b/sabot/kernel/src/main/java/com/dremio/exec/planner/sql/handlers/direct/CreateFolderRecursiveHandler.java @@ -0,0 +1,99 @@ +/* + * Copyright (C) 2017-2019 Dremio Corporation + * + * Licensed 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 com.dremio.exec.planner.sql.handlers.direct; + +import static java.util.Collections.singletonList; + +import java.util.ArrayList; +import java.util.List; + +import com.dremio.common.exceptions.UserException; +import com.dremio.exec.catalog.Catalog; +import com.dremio.exec.planner.sql.parser.SqlCreateFolderRecursive; +import com.dremio.service.namespace.NamespaceException; +import com.dremio.service.namespace.NamespaceKey; +import com.dremio.service.namespace.NamespaceService; +import com.dremio.service.namespace.space.proto.FolderConfig; +import com.dremio.service.namespace.space.proto.SpaceConfig; + +import static com.dremio.exec.planner.sql.handlers.direct.SimpleCommandResult.successful; + +import org.apache.calcite.sql.SqlNode; + + +/** + * Handler for CREATE FOLDER RECURSIVE command. + */ +public class CreateFolderRecursiveHandler extends SimpleDirectHandler { + private static final org.slf4j.Logger logger = org.slf4j.LoggerFactory.getLogger(CreateFolderRecursiveHandler.class); + + private final Catalog catalog; + private final NamespaceService namespaceService; + + public CreateFolderRecursiveHandler(Catalog catalog, NamespaceService namespaceService) { + this.catalog = catalog; + this.namespaceService = namespaceService; + } + + @Override + public List toResult(String sql, SqlNode sqlNode) throws Exception { + final SqlCreateFolderRecursive sqlRefreshSourceStatus = SqlNodeUtil.unwrap(sqlNode, SqlCreateFolderRecursive.class); + final NamespaceKey path = sqlRefreshSourceStatus.getPath(); + + String root = path.getRoot(); + if(root.startsWith("@") || root.equalsIgnoreCase("sys") || root.equalsIgnoreCase("INFORMATION_SCHEMA")) { + throw UserException.parseError().message("Unable to create spaces or folders in Home, sys, or INFORMATION_SCHEMA.", path).build(logger); + } + + String message; + try { + // Check if space exists + final NamespaceKey rootKey = new NamespaceKey(path.getRoot()); + try { + namespaceService.getSpace(rootKey); + } catch (NamespaceException nse) { + // Space does not exist, create it + SpaceConfig spaceConfig = new SpaceConfig() + .setName(path.getRoot()); + namespaceService.addOrUpdateSpace(rootKey, spaceConfig); + } + + // For each sub folder, create folder + final List folderPath = new ArrayList<>(); + folderPath.add(path.getRoot()); + for(int p=1;pCREATE FOLDER folder_path RECURSIVE + */ +public class SqlCreateFolderRecursive extends SqlSystemCall { + + public static final SqlSpecialOperator OPERATOR = + new SqlSpecialOperator("CREATE_FOLDER", SqlKind.OTHER) { + @Override public SqlCall createCall(SqlLiteral functionQualifier, + SqlParserPos pos, SqlNode... operands) { + return new SqlCreateFolderRecursive(pos, (SqlIdentifier) operands[0]); + } + }; + + private SqlIdentifier folderPath; + + /** Creates a SqlCreateFolderRecursive. */ + public SqlCreateFolderRecursive(SqlParserPos pos, SqlIdentifier source) { + super(pos); + this.folderPath = source; + } + + @Override public void unparse(SqlWriter writer, int leftPrec, int rightPrec) { + writer.keyword("CREATE"); + writer.keyword("FOLDER"); + folderPath.unparse(writer, leftPrec, rightPrec); + writer.keyword("RECURSIVE"); + } + + @Override public void setOperand(int i, SqlNode operand) { + switch (i) { + case 0: + folderPath = (SqlIdentifier) operand; + break; + default: + throw new AssertionError(i); + } + } + + @Override public SqlOperator getOperator() { + return OPERATOR; + } + + @Override public List getOperandList() { + return ImmutableNullableList.of(folderPath); + } + + public NamespaceKey getPath() { + return new NamespaceKey(folderPath.names); + } +}