This is an automated email from the ASF dual-hosted git repository. sorabh pushed a commit to branch master in repository https://gitbox.apache.org/repos/asf/drill.git
commit caa983105b0c6ea92770f0b86d7e28d457cb0bae Author: Dmytriy Grinchenko <[email protected]> AuthorDate: Wed Apr 17 12:23:34 2019 +0300 DRILL-6974: SET option command modification - ALTER ... RESET ... and ALTER ... SET ... sub-parsers separated to 2 different SqlCall classes with same parent SqlSetOption - parserImpls modified to handle new syntax of ALTER... SET... expresion: a) ALTER ... SET option.name - option.value - setting option value b) ALTER ... SET option.name - display option value - Handler for SqlSetOption separated to SetOptionHandler and ResetOptionhandler for better representation of handled statements - Base abstract class AbstractSqlSetHandler created to not repeat shared implementation of same functions - SetOptionHandler covered with unit tests for each statement form. Fix issues stated in the review closes #1763 --- exec/java-exec/src/main/codegen/data/Parser.tdd | 6 +- .../src/main/codegen/includes/parserImpls.ftl | 63 +++++++++++ .../drill/exec/planner/sql/DrillSqlWorker.java | 22 ++-- .../sql/handlers/AbstractSqlSetHandler.java | 86 +++++++++++++++ .../planner/sql/handlers/ResetOptionHandler.java | 73 +++++++++++++ .../planner/sql/handlers/SetOptionHandler.java | 119 ++++++++------------- .../sql/parser/CompoundIdentifierConverter.java | 4 +- .../planner/sql/parser/DrillSqlResetOption.java | 103 ++++++++++++++++++ .../exec/planner/sql/parser/DrillSqlSetOption.java | 75 +++++++++++++ .../sql/handlers/ResetOptionHandlerTest.java | 64 +++++++++++ .../planner/sql/handlers/SetOptionHandlerTest.java | 90 ++++++++++++++++ 11 files changed, 623 insertions(+), 82 deletions(-) diff --git a/exec/java-exec/src/main/codegen/data/Parser.tdd b/exec/java-exec/src/main/codegen/data/Parser.tdd index 3580388..df97e24 100644 --- a/exec/java-exec/src/main/codegen/data/Parser.tdd +++ b/exec/java-exec/src/main/codegen/data/Parser.tdd @@ -59,7 +59,9 @@ "SqlRefreshMetadata()", "SqlCreateFunction()", "SqlDropFunction()", - "SqlAnalyzeTable()" + "SqlAnalyzeTable()", + "DrillSqlSetOption(Span.of(), null)", + "DrillSqlResetOption(Span.of(), null)" ] # List of methods for parsing custom literals. @@ -87,6 +89,8 @@ # List of methods for parsing extensions to "ALTER <scope>" calls. # Each must accept arguments "(SqlParserPos pos, String scope)". alterStatementParserMethods: [ + "DrillSqlSetOption", + "DrillSqlResetOption" ] # List of methods for parsing extensions to "DROP" calls. diff --git a/exec/java-exec/src/main/codegen/includes/parserImpls.ftl b/exec/java-exec/src/main/codegen/includes/parserImpls.ftl index 25ee72b..f90859a 100644 --- a/exec/java-exec/src/main/codegen/includes/parserImpls.ftl +++ b/exec/java-exec/src/main/codegen/includes/parserImpls.ftl @@ -634,3 +634,66 @@ SqlNode SqlAnalyzeTable() : return new SqlAnalyzeTable(pos, tblName, estimate, fieldList, percent); } } + + +/** + * Parses a SET statement without a leading "ALTER <SCOPE>": + * + * SET <NAME> [ = VALUE ] + * <p> + * Statement handles in: {@link SetAndResetOptionHandler} + */ +DrillSqlSetOption DrillSqlSetOption(Span s, String scope) : +{ + SqlParserPos pos; + SqlIdentifier name; + SqlNode val = null; +} +{ + <SET> { + s.add(this); + } + name = CompoundIdentifier() + ( + <EQ> + ( + val = Literal() + | + val = SimpleIdentifier() + ) + )? + { + pos = (val == null) ? s.end(name) : s.end(val); + + return new DrillSqlSetOption(pos, scope, name, val); + } +} + +/** + * Parses a RESET statement without a leading "ALTER <SCOPE>": + * + * RESET { <NAME> | ALL } + * <p> + * Statement handles in: {@link SetAndResetOptionHandler} + */ +DrillSqlResetOption DrillSqlResetOption(Span s, String scope) : +{ + SqlIdentifier name; +} +{ + <RESET> { + s.add(this); + } + ( + name = CompoundIdentifier() + | + <ALL> { + name = new SqlIdentifier(token.image.toUpperCase(Locale.ROOT), getPos()); + } + ) + { + return new DrillSqlResetOption(s.end(name), scope, name); + } +} + + diff --git a/exec/java-exec/src/main/java/org/apache/drill/exec/planner/sql/DrillSqlWorker.java b/exec/java-exec/src/main/java/org/apache/drill/exec/planner/sql/DrillSqlWorker.java index 09fbbdc..5ce9c19 100644 --- a/exec/java-exec/src/main/java/org/apache/drill/exec/planner/sql/DrillSqlWorker.java +++ b/exec/java-exec/src/main/java/org/apache/drill/exec/planner/sql/DrillSqlWorker.java @@ -41,11 +41,14 @@ import org.apache.drill.exec.planner.sql.handlers.DescribeSchemaHandler; import org.apache.drill.exec.planner.sql.handlers.DescribeTableHandler; import org.apache.drill.exec.planner.sql.handlers.ExplainHandler; import org.apache.drill.exec.planner.sql.handlers.RefreshMetadataHandler; +import org.apache.drill.exec.planner.sql.handlers.ResetOptionHandler; import org.apache.drill.exec.planner.sql.handlers.SchemaHandler; import org.apache.drill.exec.planner.sql.handlers.SetOptionHandler; import org.apache.drill.exec.planner.sql.handlers.SqlHandlerConfig; import org.apache.drill.exec.planner.sql.parser.DrillSqlCall; import org.apache.drill.exec.planner.sql.parser.DrillSqlDescribeTable; +import org.apache.drill.exec.planner.sql.parser.DrillSqlResetOption; +import org.apache.drill.exec.planner.sql.parser.DrillSqlSetOption; import org.apache.drill.exec.planner.sql.parser.SqlCreateTable; import org.apache.drill.exec.planner.sql.parser.SqlSchema; import org.apache.drill.exec.testing.ControlsInjector; @@ -150,15 +153,22 @@ public class DrillSqlWorker { final AbstractSqlHandler handler; final SqlHandlerConfig config = new SqlHandlerConfig(context, parser); - switch(sqlNode.getKind()) { + switch (sqlNode.getKind()) { case EXPLAIN: handler = new ExplainHandler(config, textPlan); context.setSQLStatementType(SqlStatementType.EXPLAIN); break; case SET_OPTION: - handler = new SetOptionHandler(context); - context.setSQLStatementType(SqlStatementType.SETOPTION); - break; + if (sqlNode instanceof DrillSqlSetOption) { + handler = new SetOptionHandler(context); + context.setSQLStatementType(SqlStatementType.SETOPTION); + break; + } + if (sqlNode instanceof DrillSqlResetOption) { + handler = new ResetOptionHandler(context); + context.setSQLStatementType(SqlStatementType.SETOPTION); + break; + } case DESCRIBE_TABLE: if (sqlNode instanceof DrillSqlDescribeTable) { handler = new DescribeTableHandler(config); @@ -184,8 +194,8 @@ public class DrillSqlWorker { case DROP_VIEW: case OTHER_DDL: case OTHER: - if(sqlNode instanceof SqlCreateTable) { - handler = ((DrillSqlCall)sqlNode).getSqlHandler(config, textPlan); + if (sqlNode instanceof SqlCreateTable) { + handler = ((DrillSqlCall) sqlNode).getSqlHandler(config, textPlan); context.setSQLStatementType(SqlStatementType.CTAS); break; } diff --git a/exec/java-exec/src/main/java/org/apache/drill/exec/planner/sql/handlers/AbstractSqlSetHandler.java b/exec/java-exec/src/main/java/org/apache/drill/exec/planner/sql/handlers/AbstractSqlSetHandler.java new file mode 100644 index 0000000..71d7636 --- /dev/null +++ b/exec/java-exec/src/main/java/org/apache/drill/exec/planner/sql/handlers/AbstractSqlSetHandler.java @@ -0,0 +1,86 @@ +/* + * 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.handlers; + +import org.apache.calcite.sql.SqlSetOption; +import org.apache.drill.common.exceptions.UserException; +import org.apache.drill.exec.ExecConstants; +import org.apache.drill.exec.ops.QueryContext; +import org.apache.drill.exec.server.options.OptionValue; +import org.apache.drill.exec.server.options.QueryOptionManager; +import org.apache.drill.exec.util.ImpersonationUtil; + +/** + * Base handler for SQL_SET kind statements. + */ +abstract class AbstractSqlSetHandler extends AbstractSqlHandler { + private static final org.slf4j.Logger logger = org.slf4j.LoggerFactory.getLogger(AbstractSqlHandler.class); + + final QueryContext context; + + AbstractSqlSetHandler(QueryContext context) { + this.context = context; + } + + /** + * Extracts query {@link OptionValue.OptionScope} from the {@link SqlSetOption}. + * @param statement Statement object + * @param options Options object + * @return parsed query scope + */ + OptionValue.OptionScope getScope(SqlSetOption statement, QueryOptionManager options) { + String scope = statement.getScope(); + + if (scope == null) { + return OptionValue.OptionScope.SESSION; + } + + switch (scope.toLowerCase()) { + case "session": + if (options.getBoolean(ExecConstants.SKIP_ALTER_SESSION_QUERY_PROFILE)) { + logger.debug("Will not write profile for ALTER SESSION SET ... "); + context.skipWritingProfile(true); + } + return OptionValue.OptionScope.SESSION; + case "system": + return OptionValue.OptionScope.SYSTEM; + default: + throw UserException.validationError() + .message("Invalid OPTION scope %s. Scope must be SESSION or SYSTEM.", scope) + .build(logger); + } + } + + /** + * Admin privileges checker. + * @param options Options object + */ + void checkAdminPrivileges(QueryOptionManager options) { + if (context.isUserAuthenticationEnabled() + && !ImpersonationUtil.hasAdminPrivileges( + context.getQueryUserName(), + ExecConstants.ADMIN_USERS_VALIDATOR.getAdminUsers(options), + ExecConstants.ADMIN_USER_GROUPS_VALIDATOR.getAdminUserGroups(options))) { + + throw UserException + .permissionError() + .message("Not authorized to change SYSTEM options.") + .build(logger); + } + } +} diff --git a/exec/java-exec/src/main/java/org/apache/drill/exec/planner/sql/handlers/ResetOptionHandler.java b/exec/java-exec/src/main/java/org/apache/drill/exec/planner/sql/handlers/ResetOptionHandler.java new file mode 100644 index 0000000..8f99192 --- /dev/null +++ b/exec/java-exec/src/main/java/org/apache/drill/exec/planner/sql/handlers/ResetOptionHandler.java @@ -0,0 +1,73 @@ +/* + * 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.handlers; + +import org.apache.drill.exec.ops.QueryContext; +import org.apache.drill.exec.physical.PhysicalPlan; +import org.apache.drill.exec.planner.sql.DirectPlan; +import org.apache.drill.exec.planner.sql.parser.DrillSqlResetOption; +import org.apache.drill.exec.server.options.OptionManager; +import org.apache.drill.exec.server.options.OptionValue; +import org.apache.drill.exec.server.options.OptionValue.OptionScope; +import org.apache.drill.exec.server.options.QueryOptionManager; +import org.apache.drill.exec.work.foreman.ForemanSetupException; +import org.apache.calcite.sql.SqlNode; +import org.apache.calcite.sql.SqlSetOption; + +/** + * Converts a {@link SqlNode} representing: "ALTER .. RESET option | ALL" statement to a {@link PhysicalPlan}. + * See {@link DrillSqlResetOption}. + * <p> + * These statements have side effects i.e. the options within the system context or the session context are modified. + * The resulting {@link DirectPlan} returns to the client a string that is the name of the option that was updated + * or a value of the property + */ +public class ResetOptionHandler extends AbstractSqlSetHandler { + + /** + * Class constructor. + * @param context Context of the Query + */ + public ResetOptionHandler(QueryContext context) { + super(context); + } + + /** + * Handles {@link DrillSqlResetOption} query + */ + @Override + public final PhysicalPlan getPlan(SqlNode sqlNode) throws ForemanSetupException { + QueryOptionManager options = context.getOptions(); + SqlSetOption statement = unwrap(sqlNode, SqlSetOption.class); + OptionScope optionScope = getScope(statement, context.getOptions()); + + if (optionScope == OptionValue.OptionScope.SYSTEM) { + checkAdminPrivileges(options); + } + + OptionManager optionManager = options.getOptionManager(optionScope); + String optionName = statement.getName().toString(); + + if ("ALL".equalsIgnoreCase(optionName)) { + optionManager.deleteAllLocalOptions(); + } else { + optionManager.deleteLocalOption(optionName); + } + return DirectPlan.createDirectPlan(context, true, String.format("%s updated.", optionName)); + } +} diff --git a/exec/java-exec/src/main/java/org/apache/drill/exec/planner/sql/handlers/SetOptionHandler.java b/exec/java-exec/src/main/java/org/apache/drill/exec/planner/sql/handlers/SetOptionHandler.java index 8a6a30c..41a1b78 100644 --- a/exec/java-exec/src/main/java/org/apache/drill/exec/planner/sql/handlers/SetOptionHandler.java +++ b/exec/java-exec/src/main/java/org/apache/drill/exec/planner/sql/handlers/SetOptionHandler.java @@ -20,108 +20,68 @@ package org.apache.drill.exec.planner.sql.handlers; import java.math.BigDecimal; import org.apache.calcite.sql.type.SqlTypeName; -import org.apache.calcite.tools.ValidationException; - import org.apache.calcite.util.NlsString; import org.apache.drill.common.exceptions.UserException; -import org.apache.drill.exec.ExecConstants; import org.apache.drill.exec.ops.QueryContext; import org.apache.drill.exec.physical.PhysicalPlan; import org.apache.drill.exec.planner.sql.DirectPlan; +import org.apache.drill.exec.planner.sql.parser.DrillSqlSetOption; import org.apache.drill.exec.server.options.OptionManager; import org.apache.drill.exec.server.options.OptionValue; import org.apache.drill.exec.server.options.OptionValue.OptionScope; -import org.apache.drill.exec.server.options.QueryOptionManager; -import org.apache.drill.exec.util.ImpersonationUtil; import org.apache.drill.exec.work.foreman.ForemanSetupException; import org.apache.calcite.sql.SqlLiteral; import org.apache.calcite.sql.SqlNode; import org.apache.calcite.sql.SqlSetOption; /** - * Converts a {@link SqlNode} representing "ALTER .. SET option = value" and "ALTER ... RESET ..." statements to a - * {@link PhysicalPlan}. See {@link SqlSetOption}. These statements have side effects i.e. the options within the - * system context or the session context are modified. The resulting {@link DirectPlan} returns to the client a string - * that is the name of the option that was updated. + * Converts a {@link SqlNode} representing: "ALTER .. SET option = value" or "ALTER ... SET option" + * statement to a {@link PhysicalPlan}. See {@link DrillSqlSetOption} + * <p> + * These statements have side effects i.e. the options within the system context or the session context are modified. + * The resulting {@link DirectPlan} returns to the client a string that is the name of the option that was updated + * or a value of the property */ -public class SetOptionHandler extends AbstractSqlHandler { +public class SetOptionHandler extends AbstractSqlSetHandler { private static final org.slf4j.Logger logger = org.slf4j.LoggerFactory.getLogger(SetOptionHandler.class); - private final QueryContext context; - public SetOptionHandler(QueryContext context) { - this.context = context; + super(context); } + /** + * Handles {@link DrillSqlSetOption} query + */ @Override - public PhysicalPlan getPlan(SqlNode sqlNode) throws ValidationException, ForemanSetupException { - final SqlSetOption option = unwrap(sqlNode, SqlSetOption.class); - final SqlNode value = option.getValue(); - if (value != null && !(value instanceof SqlLiteral)) { - throw UserException.validationError() - .message("Drill does not support assigning non-literal values in SET statements.") - .build(logger); - } + public final PhysicalPlan getPlan(SqlNode sqlNode) throws ForemanSetupException { + // sqlNode could contain DrillSqlResetOption or DrillSqlSetOption, depends on parsed statement + SqlSetOption statement = unwrap(sqlNode, SqlSetOption.class); + OptionScope optionScope = getScope(statement, context.getOptions()); + OptionManager optionManager = context.getOptions().getOptionManager(optionScope); + + String optionName = statement.getName().toString(); + SqlNode optionValue = statement.getValue(); + + if (optionValue == null) { + String value = String.valueOf(optionManager.getOption(optionName).getValue()); - final QueryOptionManager options = context.getOptions(); - final String scope = option.getScope(); - final OptionValue.OptionScope optionScope; - if (scope == null) { // No scope mentioned assumed SESSION - optionScope = OptionScope.SESSION; + return DirectPlan.createDirectPlan(context, new SetOptionViewResult(optionName, value)); } else { - switch (scope.toLowerCase()) { - case "session": - optionScope = OptionScope.SESSION; - // Skip writing profiles for "ALTER SESSION SET" queries - if (options.getBoolean(ExecConstants.SKIP_ALTER_SESSION_QUERY_PROFILE)) { - logger.debug("Will not write profile for ALTER SESSION SET ... "); - context.skipWritingProfile(true); - } - break; - case "system": - optionScope = OptionScope.SYSTEM; - break; - default: + if (optionScope == OptionValue.OptionScope.SYSTEM) { + checkAdminPrivileges(context.getOptions()); + } + if (!(optionValue instanceof SqlLiteral)) { throw UserException.validationError() - .message("Invalid OPTION scope %s. Scope must be SESSION or SYSTEM.", scope) + .message("Drill does not support assigning non-literal values in SET statements.") .build(logger); } - } + optionManager.setLocalOption(optionName, sqlLiteralToObject((SqlLiteral) optionValue)); - if (optionScope == OptionScope.SYSTEM) { - // If the user authentication is enabled, make sure the user who is trying to change the system option has - // administrative privileges. - if (context.isUserAuthenticationEnabled() && - !ImpersonationUtil.hasAdminPrivileges( - context.getQueryUserName(), - ExecConstants.ADMIN_USERS_VALIDATOR.getAdminUsers(options), - ExecConstants.ADMIN_USER_GROUPS_VALIDATOR.getAdminUserGroups(options))) { - throw UserException.permissionError() - .message("Not authorized to change SYSTEM options.") - .build(logger); - } + return DirectPlan.createDirectPlan(context, true, String.format("%s updated.", optionName)); } - - final String optionName = option.getName().toString(); - - // Currently, we convert multi-part identifier to a string. - final OptionManager chosenOptions = options.getOptionManager(optionScope); - - if (value != null) { // SET option - final Object literalObj = sqlLiteralToObject((SqlLiteral) value); - chosenOptions.setLocalOption(optionName, literalObj); - } else { // RESET option - if ("ALL".equalsIgnoreCase(optionName)) { - chosenOptions.deleteAllLocalOptions(); - } else { - chosenOptions.deleteLocalOption(optionName); - } - } - - return DirectPlan.createDirectPlan(context, true, String.format("%s updated.", optionName)); } - private static Object sqlLiteralToObject(final SqlLiteral literal) { + private static Object sqlLiteralToObject(SqlLiteral literal) { final Object object = literal.getValue(); final SqlTypeName typeName = literal.getTypeName(); switch (typeName) { @@ -147,7 +107,7 @@ public class SetOptionHandler extends AbstractSqlHandler { case VARBINARY: case VARCHAR: case CHAR: - return ((NlsString) object).getValue().toString(); + return ((NlsString) object).getValue(); case BOOLEAN: return object; @@ -158,4 +118,17 @@ public class SetOptionHandler extends AbstractSqlHandler { .build(logger); } } + + /** + * Representation of "SET property.name" query result. + */ + public static class SetOptionViewResult { + public String name; + public String value; + + SetOptionViewResult(String name, String value) { + this.name = name; + this.value = value; + } + } } diff --git a/exec/java-exec/src/main/java/org/apache/drill/exec/planner/sql/parser/CompoundIdentifierConverter.java b/exec/java-exec/src/main/java/org/apache/drill/exec/planner/sql/parser/CompoundIdentifierConverter.java index 119b27d..ac0d163 100644 --- a/exec/java-exec/src/main/java/org/apache/drill/exec/planner/sql/parser/CompoundIdentifierConverter.java +++ b/exec/java-exec/src/main/java/org/apache/drill/exec/planner/sql/parser/CompoundIdentifierConverter.java @@ -27,7 +27,6 @@ import org.apache.calcite.sql.SqlKind; import org.apache.calcite.sql.SqlNode; import org.apache.calcite.sql.SqlOrderBy; import org.apache.calcite.sql.SqlSelect; -import org.apache.calcite.sql.SqlSetOption; import org.apache.calcite.sql.util.SqlShuttle; import org.apache.calcite.sql.util.SqlVisitor; @@ -77,7 +76,8 @@ public class CompoundIdentifierConverter extends SqlShuttle { .put(SqlOrderBy.class, arrayOf(D, E, D, D)) .put(SqlDropTable.class, arrayOf(D, D)) .put(SqlRefreshMetadata.class, arrayOf(D, D, E)) - .put(SqlSetOption.class, arrayOf(D, D, D)) + .put(DrillSqlSetOption.class, arrayOf(D, D, D)) + .put(DrillSqlResetOption.class, arrayOf(D, D)) .put(SqlCreateFunction.class, arrayOf(D)) .put(SqlDropFunction.class, arrayOf(D)) .put(SqlSchema.Create.class, arrayOf(D, D, D, D, D, D)) diff --git a/exec/java-exec/src/main/java/org/apache/drill/exec/planner/sql/parser/DrillSqlResetOption.java b/exec/java-exec/src/main/java/org/apache/drill/exec/planner/sql/parser/DrillSqlResetOption.java new file mode 100644 index 0000000..18c199c --- /dev/null +++ b/exec/java-exec/src/main/java/org/apache/drill/exec/planner/sql/parser/DrillSqlResetOption.java @@ -0,0 +1,103 @@ +/* + * 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 java.util.ArrayList; +import java.util.List; + +import org.apache.calcite.sql.SqlCall; +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.SqlOperator; +import org.apache.calcite.sql.SqlSetOption; +import org.apache.calcite.sql.SqlSpecialOperator; +import org.apache.calcite.sql.SqlWriter; +import org.apache.calcite.sql.parser.SqlParserPos; +import org.apache.calcite.util.ImmutableNullableList; +import org.apache.drill.exec.planner.sql.handlers.ResetOptionHandler; + +/** + * Sql parse tree node to represent statement: {@code RESET { <NAME> | ALL } }. + * Statement handled in: {@link ResetOptionHandler} + */ +public final class DrillSqlResetOption extends SqlSetOption { + + public static final SqlSpecialOperator OPERATOR = new SqlSpecialOperator("RESET_OPTION", SqlKind.SET_OPTION) { + @Override + public SqlCall createCall(SqlLiteral functionQualifier, SqlParserPos pos, SqlNode... operands) { + SqlNode scopeNode = operands[0]; + return new DrillSqlResetOption(pos, scopeNode == null ? null : scopeNode.toString(), (SqlIdentifier) operands[1]); + } + }; + + public DrillSqlResetOption(SqlParserPos pos, String scope, SqlIdentifier name) { + super(pos, scope, name, null); + } + + @Override + public SqlKind getKind() { + return SqlKind.SET_OPTION; + } + + @Override + public SqlOperator getOperator() { + return OPERATOR; + } + + @Override + public List<SqlNode> getOperandList() { + List<SqlNode> operandList = new ArrayList<>(); + + SqlIdentifier scopeIdentifier = (this.getScope() == null) ? null : new SqlIdentifier(this.getScope(), + SqlParserPos.ZERO); + + operandList.add(scopeIdentifier); + operandList.add(this.getName()); + return ImmutableNullableList.copyOf(operandList); + } + + @Override + public void setOperand(int i, SqlNode operand) { + switch (i) { + case 0: + if (operand != null) { + this.setScope(((SqlIdentifier) operand).getSimple()); + } else { + this.setScope(null); + } + break; + case 1: + this.setName((SqlIdentifier) operand); + break; + default: + throw new AssertionError(i); + } + } + + @Override + protected void unparseAlterOperation(SqlWriter writer, int leftPrec, int rightPrec) { + writer.keyword("RESET"); + + SqlWriter.Frame frame = writer.startList(SqlWriter.FrameTypeEnum.SIMPLE); + + this.getName().unparse(writer, leftPrec, rightPrec); + writer.endList(frame); + } +} diff --git a/exec/java-exec/src/main/java/org/apache/drill/exec/planner/sql/parser/DrillSqlSetOption.java b/exec/java-exec/src/main/java/org/apache/drill/exec/planner/sql/parser/DrillSqlSetOption.java new file mode 100644 index 0000000..8cf5f77 --- /dev/null +++ b/exec/java-exec/src/main/java/org/apache/drill/exec/planner/sql/parser/DrillSqlSetOption.java @@ -0,0 +1,75 @@ +/* + * 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.SqlIdentifier; +import org.apache.calcite.sql.SqlKind; +import org.apache.calcite.sql.SqlLiteral; +import org.apache.calcite.sql.SqlNode; +import org.apache.calcite.sql.SqlOperator; +import org.apache.calcite.sql.SqlSetOption; +import org.apache.calcite.sql.SqlSpecialOperator; +import org.apache.calcite.sql.SqlWriter; +import org.apache.calcite.sql.parser.SqlParserPos; +import org.apache.drill.exec.planner.sql.handlers.SetOptionHandler; + +/** + * Sql parse tree node to represent statement: {@code SET <NAME> [ = VALUE ]}. + * Statement handled in: {@link SetOptionHandler} + */ +public final class DrillSqlSetOption extends SqlSetOption { + + public static final SqlSpecialOperator OPERATOR = new SqlSpecialOperator("SET_OPTION", SqlKind.SET_OPTION) { + @Override + public SqlCall createCall(SqlLiteral functionQualifier, SqlParserPos pos, SqlNode... operands) { + SqlNode scopeNode = operands[0]; + String scope = scopeNode == null ? null : scopeNode.toString(); + return new DrillSqlSetOption(pos, scope, (SqlIdentifier) operands[1], operands[2]); + } + }; + + public DrillSqlSetOption(SqlParserPos pos, String scope, SqlIdentifier name, SqlNode value) { + super(pos, scope, name, value); + } + + @Override + public SqlKind getKind() { + return SqlKind.SET_OPTION; + } + + @Override + public SqlOperator getOperator() { + return OPERATOR; + } + + @Override + protected void unparseAlterOperation(SqlWriter writer, int leftPrec, int rightPrec) { + writer.keyword("SET"); + + SqlWriter.Frame frame = writer.startList(SqlWriter.FrameTypeEnum.SIMPLE); + this.getName().unparse(writer, leftPrec, rightPrec); + + if (this.getValue() != null) { + writer.sep("="); + this.getValue().unparse(writer, leftPrec, rightPrec); + } + + writer.endList(frame); + } +} diff --git a/exec/java-exec/src/test/java/org/apache/drill/exec/planner/sql/handlers/ResetOptionHandlerTest.java b/exec/java-exec/src/test/java/org/apache/drill/exec/planner/sql/handlers/ResetOptionHandlerTest.java new file mode 100644 index 0000000..cc3d4a2 --- /dev/null +++ b/exec/java-exec/src/test/java/org/apache/drill/exec/planner/sql/handlers/ResetOptionHandlerTest.java @@ -0,0 +1,64 @@ +/* + * 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.handlers; + +import org.apache.drill.categories.SqlTest; +import org.apache.drill.exec.ExecConstants; +import org.apache.drill.test.ClusterFixture; +import org.apache.drill.test.ClusterTest; +import org.junit.BeforeClass; +import org.junit.Test; +import org.junit.experimental.categories.Category; + +@Category(SqlTest.class) +public class ResetOptionHandlerTest extends ClusterTest { + + @BeforeClass + public static void setup() throws Exception { + startCluster(ClusterFixture.builder(dirTestWatcher).maxParallelization(1)); + } + + @Test + public void testReset() throws Exception { + int defaultValue = Integer.valueOf(client.queryBuilder() + .sql("SELECT val from sys.options where name = '%s' limit 1", ExecConstants.CODE_GEN_EXP_IN_METHOD_SIZE) + .singletonString()); + + int testValue = defaultValue + 55; + + try { + client.alterSession(ExecConstants.CODE_GEN_EXP_IN_METHOD_SIZE, testValue); + client.testBuilder() + .sqlQuery("select name, val from sys.options where name = '%s'", ExecConstants.CODE_GEN_EXP_IN_METHOD_SIZE) + .unOrdered() + .baselineColumns("name", "val") + .baselineValues(ExecConstants.CODE_GEN_EXP_IN_METHOD_SIZE, String.valueOf(testValue)) + .go(); + + client.resetSession(ExecConstants.CODE_GEN_EXP_IN_METHOD_SIZE); + client.testBuilder() + .sqlQuery("select name, val from sys.options where name = '%s'", ExecConstants.CODE_GEN_EXP_IN_METHOD_SIZE) + .unOrdered() + .baselineColumns("name", "val") + .baselineValues(ExecConstants.CODE_GEN_EXP_IN_METHOD_SIZE, String.valueOf(defaultValue)) + .go(); + } finally { + client.resetSession(ExecConstants.CODE_GEN_EXP_IN_METHOD_SIZE); + } + } +} diff --git a/exec/java-exec/src/test/java/org/apache/drill/exec/planner/sql/handlers/SetOptionHandlerTest.java b/exec/java-exec/src/test/java/org/apache/drill/exec/planner/sql/handlers/SetOptionHandlerTest.java new file mode 100644 index 0000000..def1240 --- /dev/null +++ b/exec/java-exec/src/test/java/org/apache/drill/exec/planner/sql/handlers/SetOptionHandlerTest.java @@ -0,0 +1,90 @@ +/* + * 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.handlers; + +import org.apache.drill.categories.SqlTest; +import org.apache.drill.exec.ExecConstants; +import org.apache.drill.exec.compile.ClassCompilerSelector; +import org.apache.drill.test.ClusterFixture; +import org.apache.drill.test.ClusterTest; +import org.junit.Assert; +import org.junit.BeforeClass; +import org.junit.Test; +import org.junit.experimental.categories.Category; + +@Category(SqlTest.class) +public class SetOptionHandlerTest extends ClusterTest { + + @BeforeClass + public static void setup() throws Exception { + startCluster(ClusterFixture.builder(dirTestWatcher).maxParallelization(1)); + } + + @Test + public void testSimpleSetQuery() throws Exception { + String defaultValue = client.queryBuilder() + .sql("SELECT val from sys.options where name = '%s' limit 1", + ClassCompilerSelector.JAVA_COMPILER_DEBUG_OPTION) + .singletonString(); + + boolean newValue = !Boolean.valueOf(defaultValue); + try { + client.alterSession(ClassCompilerSelector.JAVA_COMPILER_DEBUG_OPTION, newValue); + + String changedValue = client.queryBuilder() + .sql("SELECT val from sys.options where name = '%s' limit 1", + ClassCompilerSelector.JAVA_COMPILER_DEBUG_OPTION) + .singletonString(); + + Assert.assertEquals(String.valueOf(newValue), changedValue); + } finally { + client.resetSession(ClassCompilerSelector.JAVA_COMPILER_DEBUG_OPTION); + } + } + + @Test + public void testViewSetQuery() throws Exception { + client.testBuilder() // BIT + .sqlQuery("SET `%s`", ExecConstants.ENABLE_ITERATOR_VALIDATION_OPTION) + .unOrdered() + .sqlBaselineQuery("SELECT name, val as value FROM sys.options where name = '%s' limit 1", + ExecConstants.ENABLE_ITERATOR_VALIDATION_OPTION) + .go(); + + client.testBuilder() // BIGINT + .sqlQuery("SET `%s`", ExecConstants.OUTPUT_BATCH_SIZE) + .unOrdered() + .sqlBaselineQuery("SELECT name, val as value FROM sys.options where name = '%s' limit 1", + ExecConstants.OUTPUT_BATCH_SIZE) + .go(); + + client.testBuilder() // FLOAT + .sqlQuery("SET `%s`", ExecConstants.OUTPUT_BATCH_SIZE_AVAIL_MEM_FACTOR) + .unOrdered() + .sqlBaselineQuery("SELECT name, val as value FROM sys.options where name = '%s' limit 1", + ExecConstants.OUTPUT_BATCH_SIZE_AVAIL_MEM_FACTOR) + .go(); + + client.testBuilder() // VARCHAR + .sqlQuery("SET `%s`", ExecConstants.FILESYSTEM_PARTITION_COLUMN_LABEL) + .unOrdered() + .sqlBaselineQuery("SELECT name, val as value FROM sys.options where name = '%s' limit 1", + ExecConstants.FILESYSTEM_PARTITION_COLUMN_LABEL) + .go(); + } +}
