blob: f5a37c79b91b5e3130e1fe4365e103b27f61dabd [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.store.sys;
import java.util.Comparator;
import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import org.apache.drill.exec.ops.FragmentContext;
import org.apache.drill.exec.server.options.OptionManager;
import org.apache.drill.exec.server.options.OptionValue;
import org.apache.drill.exec.server.options.OptionValidator.OptionDescription;
import org.apache.drill.exec.server.options.OptionValue.AccessibleScopes;
import org.apache.drill.exec.server.options.OptionValue.Kind;
import org.apache.drill.exec.server.options.OptionValue.OptionScope;
import org.apache.drill.exec.store.pojo.NonNullable;
import com.google.common.collect.Lists;
/**
* Extends the original Option iterator. The idea is to hide the implementation details and present the
* user with the rows which have values set at the top level of hierarchy and exclude the values set
* at lower levels. This is done by examining the scope and the precedence order of scope is session - system - default.
* All the values are represented as String instead of having multiple
* columns and the data type is provided as kind to preserve type information about the option.
* The query output is as follows -
* name,kind,type,val,optionScope
* planner.slice_target,BIGINT,SESSION,20,SESSION
* planner.width.max_per_node,BIGINT,SYSTEM,0,BOOT
* planner.affinity_factor,FLOAT,SYSTEM,1.7,SYSTEM
* In the above example, the query output contains single row for each option
* and we can infer that slice target is set at the session level and max width
* per node is set at the BOOT level and affinity factor is set at the SYSTEM level.
* The options set in the top level of hierarchy always takes precedence and they are returned
* in the query output. For example if the option is set at both SESSION level and
* SYSTEM level the value set at SESSION level takes precedence and query output has
* only the value set at SESSION level.
*/
public class ExtendedOptionIterator implements Iterator<Object> {
private final OptionManager fragmentOptions;
private final Iterator<OptionValue> mergedOptions;
private final Map<OptionValue.Kind, String> typeMapping;
private static final int SHORT_DESCRIP_MAX_SIZE = 110;
public ExtendedOptionIterator(FragmentContext context, boolean internal) {
fragmentOptions = context.getOptions();
typeMapping = new HashMap<>();
typeMapping.put(Kind.STRING, "VARCHAR");
typeMapping.put(Kind.DOUBLE, "FLOAT");
typeMapping.put(Kind.LONG, "BIGINT");
typeMapping.put(Kind.BOOLEAN, "BIT");
if (!internal) {
mergedOptions = sortOptions(fragmentOptions.getPublicOptionList().iterator());
} else {
mergedOptions = sortOptions(fragmentOptions.getInternalOptionList().iterator());
}
}
/* *
* Remove the redundant rows for the same option based on the scope and return
* the value that takes precedence over others. For example the option set in session
* scope takes precedence over system and boot and etc.,
*/
public Iterator<OptionValue> sortOptions(Iterator<OptionValue> options) {
List<OptionValue> optionslist = Lists.newArrayList(options);
HashMap<String, OptionValue> optionsmap = new HashMap<>();
for (OptionValue option : optionslist) {
if (option.scope == OptionScope.QUERY) {
// Option set on query level should be ignored here as its value should not be shown to user
continue;
}
if (optionsmap.containsKey(option.getName())) {
if (option.scope.compareTo(optionsmap.get(option.getName()).scope) > 0) {
optionsmap.put(option.getName(), option);
}
} else {
optionsmap.put(option.getName(), option);
}
}
optionslist.clear();
for (String name : optionsmap.keySet()) {
optionslist.add(optionsmap.get(name));
}
optionslist.sort(Comparator.comparing(OptionValue::getName));
return optionslist.iterator();
}
@Override
public boolean hasNext() {
return mergedOptions.hasNext();
}
@Override
public ExtendedOptionValueWrapper next() {
final OptionValue value = mergedOptions.next();
final Status status;
if (value.accessibleScopes == AccessibleScopes.BOOT) {
status = Status.BOOT;
} else {
final OptionValue def = fragmentOptions.getDefault(value.name);
status = (value.equalsIgnoreType(def) ? Status.DEFAULT : Status.CHANGED);
}
return new ExtendedOptionValueWrapper(value.name, typeMapping.get(value.kind), value.accessibleScopes,value.getValue().toString(), status, value.scope,
getShortDescription(value.name));
}
public enum Status {
BOOT, DEFAULT, CHANGED
}
/**
* Wrapper class for Extended Option Value
*/
public static class ExtendedOptionValueWrapper {
@NonNullable
public final String name;
@NonNullable
public final String kind;
@NonNullable
public final OptionValue.AccessibleScopes accessibleScopes;
public final String val;
public final Status status;
@NonNullable
public final OptionScope optionScope;
public final String description;
public ExtendedOptionValueWrapper(final String name, final String kind, final OptionValue.AccessibleScopes type, final String value, final Status status, final OptionScope scope,
final String description) {
this.name = name;
this.kind = kind;
this.accessibleScopes = type;
this.val = value;
this.status = status;
this.optionScope = scope;
this.description = description;
}
}
//Extract a limited length from the original description if not available
private String getShortDescription(String name) {
OptionDescription optionDescription = fragmentOptions.getOptionDefinition(name).getValidator().getOptionDescription();
if (optionDescription == null) {
return "";
}
String description;
if (optionDescription.hasShortDescription()) {
description = optionDescription.getShortDescription();
} else {
description = optionDescription.getDescription();
if (description.length() > SHORT_DESCRIP_MAX_SIZE) {
return description.substring(0, SHORT_DESCRIP_MAX_SIZE-3).concat("..."); //Append ellipsis (trailing dots)
}
}
return description;
}
@Override
public void remove() {
throw new UnsupportedOperationException();
}
}