SqlSetOption.java
/*
* 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.calcite.sql;
import org.apache.calcite.sql.parser.SqlParserPos;
import org.apache.calcite.sql.validate.SqlValidator;
import org.apache.calcite.sql.validate.SqlValidatorScope;
import org.apache.calcite.util.ImmutableNullableList;
import org.checkerframework.checker.nullness.qual.Nullable;
import java.util.ArrayList;
import java.util.List;
import static java.util.Objects.requireNonNull;
/**
* SQL parse tree node to represent {@code SET} and {@code RESET} statements,
* optionally preceded by {@code ALTER SYSTEM} or {@code ALTER SESSION}.
*
* <p>Syntax:
*
* <blockquote><code>
* ALTER scope SET `option.name` = value;<br>
* ALTER scope RESET `option`.`name`;<br>
* ALTER scope RESET ALL;<br>
* <br>
* SET `option.name` = value;<br>
* RESET `option`.`name`;<br>
* RESET ALL;
* </code></blockquote>
*
* <p>If {@link #scope} is null, assume a default scope. (The default scope
* is defined by the project using Calcite, but is typically SESSION.)
*
* <p>If {@link #value} is null, assume RESET;
* if {@link #value} is not null, assume SET.
*
* <p>Examples:
*
* <ul>
* <li><code>ALTER SYSTEM SET `my`.`param1` = 1</code></li>
* <li><code>SET `my.param2` = 1</code></li>
* <li><code>SET `my.param3` = ON</code></li>
* <li><code>ALTER SYSTEM RESET `my`.`param1`</code></li>
* <li><code>RESET `my.param2`</code></li>
* <li><code>ALTER SESSION RESET ALL</code></li>
* </ul>
*/
public class SqlSetOption extends SqlAlter {
public static final SqlSpecialOperator OPERATOR =
new SqlSpecialOperator("SET_OPTION", SqlKind.SET_OPTION) {
@SuppressWarnings("argument.type.incompatible")
@Override public SqlCall createCall(@Nullable SqlLiteral functionQualifier,
SqlParserPos pos, @Nullable SqlNode... operands) {
final SqlNode scopeNode = operands[0];
return new SqlSetOption(pos,
scopeNode == null ? null : scopeNode.toString(),
operands[1], operands[2]);
}
};
/** Name of the option as an {@link org.apache.calcite.sql.SqlIdentifier}
* with one or more parts.*/
@Deprecated // to be removed before 2.0
SqlIdentifier name;
/**
* Name of the option as an {@link SqlNode}.
*
* <p>{@link org.apache.calcite.sql.SqlIdentifier} can not be used
* as a name parameter. For example, in PostgreSQL, these two SQL commands
* have different meanings:
* <ul>
* <li><code>RESET ALL</code> resets all settable run-time parameters to default values.</li>
* <li><code>RESET "ALL"</code> resets parameter "ALL".</li>
* </ul>
* Using only {@link org.apache.calcite.sql.SqlIdentifier} makes
* it impossible to distinguish which case is being referred to.
*
* <p>TODO: Rename to 'name' when deprecated `name` field is removed.
*/
SqlNode nameAsSqlNode;
/** Value of the option. May be a {@link org.apache.calcite.sql.SqlLiteral} or
* a {@link org.apache.calcite.sql.SqlIdentifier} with one
* part. Reserved words (currently just 'ON') are converted to
* identifiers by the parser. */
@Nullable SqlNode value;
/**
* Creates a node.
*
* @param pos Parser position, must not be null.
* @param scope Scope (generally "SYSTEM" or "SESSION"), may be null.
* @param name Name of option, must not be null.
* @param value Value of option, as an identifier or literal, may be null.
* If null, assume RESET command, else assume SET command.
*/
public SqlSetOption(SqlParserPos pos, @Nullable String scope, SqlNode name,
@Nullable SqlNode value) {
super(pos, scope);
this.scope = scope;
this.name = requireNonNull(name, "name") instanceof SqlIdentifier ? (SqlIdentifier) name
: new SqlIdentifier(name.toString(), name.getParserPosition());
this.nameAsSqlNode = name;
this.value = value;
}
@Deprecated // to be removed before 2.0
public SqlSetOption(SqlParserPos pos, @Nullable String scope, SqlIdentifier name,
@Nullable SqlNode value) {
this(pos, scope, (SqlNode) name, value);
}
@Override public SqlKind getKind() {
return SqlKind.SET_OPTION;
}
@Override public SqlOperator getOperator() {
return OPERATOR;
}
@SuppressWarnings("nullness")
@Override public List<SqlNode> getOperandList() {
final List<@Nullable SqlNode> operandList = new ArrayList<>();
if (scope == null) {
operandList.add(null);
} else {
operandList.add(new SqlIdentifier(scope, SqlParserPos.ZERO));
}
operandList.add(name);
operandList.add(value);
return ImmutableNullableList.copyOf(operandList);
}
@Override public void setOperand(int i, @Nullable SqlNode operand) {
switch (i) {
case 0:
if (operand != null) {
scope = ((SqlIdentifier) operand).getSimple();
} else {
scope = null;
}
break;
case 1:
setName(requireNonNull(operand, "operand"));
break;
case 2:
value = operand;
break;
default:
throw new AssertionError(i);
}
}
@Override public void unparse(final SqlWriter writer, final int leftPrec, final int rightPrec) {
writer.getDialect().unparseSqlSetOption(writer, leftPrec, rightPrec, this);
}
@Override protected void unparseAlterOperation(SqlWriter writer, int leftPrec, int rightPrec) {
throw new UnsupportedOperationException();
}
@Override public void validate(SqlValidator validator,
SqlValidatorScope scope) {
if (value != null) {
validator.validate(value);
}
}
@Deprecated // to be removed before 2.0
public SqlIdentifier getName() {
return name;
}
// TODO: Rename to 'getName' when deprecated `getName` method is removed.
public SqlNode name() {
return nameAsSqlNode;
}
@Deprecated // to be removed before 2.0
public void setName(SqlIdentifier name) {
this.name = name;
}
public void setName(SqlNode name) {
this.name =
name instanceof SqlIdentifier ? (SqlIdentifier) name
: new SqlIdentifier(name.toString(), name.getParserPosition());
this.nameAsSqlNode = name;
}
public @Nullable SqlNode getValue() {
return value;
}
public void setValue(SqlNode value) {
this.value = value;
}
}