com.hazelcast.org.apache.calcite.sql.SqlHint 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 com.hazelcast.org.apache.calcite.sql;
import com.hazelcast.org.apache.calcite.sql.parser.SqlParserPos;
import com.hazelcast.org.apache.calcite.util.NlsString;
import com.hazelcast.com.google.common.collect.ImmutableList;
import com.hazelcast.com.google.common.collect.ImmutableMap;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.stream.Collectors;
/**
* A SqlHint
is a node of a parse tree which represents
* a sql hint expression.
*
* Basic hint grammar is: hint_name[(option1, option2 ...)].
* The hint_name should be a simple identifier, the options part is optional.
* Every option can be of four formats:
*
*
* - simple identifier
* - literal
* - key value pair whose key is a simple identifier and value is a string literal
* - key value pair whose key and value are both string literal
*
*
* The option format can not be mixed in, they should either be all simple identifiers
* or all literals or all key value pairs.
*
*
We support 2 kinds of hints in the parser:
*
* - Query hint, right after the select keyword, i.e.:
*
* select /*+ hint1, hint2, ... */ ...
*
*
* - Table hint: right after the referenced table name, i.e.:
*
* select f0, f1, f2 from t1 /*+ hint1, hint2, ... */ ...
*
*
*
*/
public class SqlHint extends SqlCall {
//~ Instance fields --------------------------------------------------------
private final SqlIdentifier name;
private final SqlNodeList options;
private final HintOptionFormat optionFormat;
private static final SqlOperator OPERATOR =
new SqlSpecialOperator("HINT", SqlKind.HINT);
//~ Constructors -----------------------------------------------------------
public SqlHint(
SqlParserPos pos,
SqlIdentifier name,
SqlNodeList options,
HintOptionFormat optionFormat) {
super(pos);
this.name = name;
this.optionFormat = optionFormat;
this.options = options;
}
//~ Methods ----------------------------------------------------------------
public SqlOperator getOperator() {
return OPERATOR;
}
public List getOperandList() {
return ImmutableList.of(name, options);
}
/**
* Returns the sql hint name.
*/
public String getName() {
return name.getSimple();
}
/** Returns the hint option format. */
public HintOptionFormat getOptionFormat() {
return optionFormat;
}
/**
* Returns a string list if the hint option is a list of
* simple SQL identifier, or a list of literals,
* else returns an empty list.
*/
public List getOptionList() {
if (optionFormat == HintOptionFormat.ID_LIST) {
final List attrs = options.getList().stream()
.map(node -> ((SqlIdentifier) node).getSimple())
.collect(Collectors.toList());
return ImmutableList.copyOf(attrs);
} else if (optionFormat == HintOptionFormat.LITERAL_LIST) {
final List attrs = options.getList().stream()
.map(node -> {
SqlLiteral literal = (SqlLiteral) node;
Comparable> comparable = SqlLiteral.value(literal);
return comparable instanceof NlsString
? ((NlsString) comparable).getValue()
: comparable.toString();
})
.collect(Collectors.toList());
return ImmutableList.copyOf(attrs);
} else {
return ImmutableList.of();
}
}
/**
* Returns a key value string map if the hint option is a list of
* pair, each pair contains a simple SQL identifier and a string literal;
* else returns an empty map.
*/
public Map getOptionKVPairs() {
if (optionFormat == HintOptionFormat.KV_LIST) {
final Map attrs = new HashMap<>();
for (int i = 0; i < options.size() - 1; i += 2) {
final SqlNode k = options.get(i);
final SqlNode v = options.get(i + 1);
attrs.put(getOptionKeyAsString(k), ((SqlLiteral) v).getValueAs(String.class));
}
return ImmutableMap.copyOf(attrs);
} else {
return ImmutableMap.of();
}
}
@Override public void unparse(SqlWriter writer, int leftPrec, int rightPrec) {
name.unparse(writer, leftPrec, rightPrec);
if (this.options.size() > 0) {
SqlWriter.Frame frame = writer.startList(SqlWriter.FrameTypeEnum.FUN_CALL, "(", ")");
for (int i = 0; i < options.size(); i++) {
SqlNode option = options.get(i);
SqlNode nextOption = i < options.size() - 1 ? options.get(i + 1) : null;
writer.sep(",", false);
option.unparse(writer, leftPrec, rightPrec);
if (optionFormat == HintOptionFormat.KV_LIST && nextOption != null) {
writer.keyword("=");
nextOption.unparse(writer, leftPrec, rightPrec);
i += 1;
}
}
writer.endList(frame);
}
}
/** Enumeration that represents hint option format. */
public enum HintOptionFormat {
/**
* The hint has no options.
*/
EMPTY,
/**
* The hint options are as literal list.
*/
LITERAL_LIST,
/**
* The hint options are as simple identifier list.
*/
ID_LIST,
/**
* The hint options are list of key-value pairs.
* For each pair,
* the key is a simple identifier or string literal,
* the value is a string literal.
*/
KV_LIST
}
//~ Tools ------------------------------------------------------------------
private static String getOptionKeyAsString(SqlNode node) {
assert node instanceof SqlIdentifier || SqlUtil.isLiteral(node);
if (node instanceof SqlIdentifier) {
return ((SqlIdentifier) node).getSimple();
}
return ((SqlLiteral) node).getValueAs(String.class);
}
}