All Downloads are FREE. Search and download functionalities are using the official Maven repository.

org.apache.druid.sql.calcite.planner.PlannerConfig Maven / Gradle / Ivy

There is a newer version: 31.0.0
Show newest version
/*
 * 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.druid.sql.calcite.planner;

import com.fasterxml.jackson.annotation.JsonProperty;
import org.apache.druid.java.util.common.UOE;
import org.apache.druid.query.QueryContexts;
import org.joda.time.DateTimeZone;

import java.util.Map;
import java.util.Objects;

public class PlannerConfig
{
  public static final String CTX_KEY_USE_APPROXIMATE_COUNT_DISTINCT = "useApproximateCountDistinct";
  public static final String CTX_KEY_USE_GROUPING_SET_FOR_EXACT_DISTINCT = "useGroupingSetForExactDistinct";
  public static final String CTX_KEY_USE_APPROXIMATE_TOPN = "useApproximateTopN";
  public static final String CTX_COMPUTE_INNER_JOIN_COST_AS_FILTER = "computeInnerJoinCostAsFilter";
  public static final String CTX_KEY_USE_NATIVE_QUERY_EXPLAIN = "useNativeQueryExplain";
  public static final String CTX_KEY_FORCE_EXPRESSION_VIRTUAL_COLUMNS = "forceExpressionVirtualColumns";
  public static final String CTX_MAX_NUMERIC_IN_FILTERS = "maxNumericInFilters";
  public static final String CTX_NATIVE_QUERY_SQL_PLANNING_MODE = "plannerStrategy";
  public static final int NUM_FILTER_NOT_USED = -1;

  @JsonProperty
  private int maxTopNLimit = 100_000;

  @JsonProperty
  private boolean useApproximateCountDistinct = true;

  @JsonProperty
  private boolean useApproximateTopN = true;

  @JsonProperty
  private boolean requireTimeCondition = false;

  @JsonProperty
  private DateTimeZone sqlTimeZone = DateTimeZone.UTC;

  @JsonProperty
  private boolean useGroupingSetForExactDistinct = false;

  @JsonProperty
  private boolean computeInnerJoinCostAsFilter = true;

  @JsonProperty
  private boolean authorizeSystemTablesDirectly = false;

  @JsonProperty
  private boolean useNativeQueryExplain = true;

  @JsonProperty
  private boolean forceExpressionVirtualColumns = false;

  @JsonProperty
  private int maxNumericInFilters = NUM_FILTER_NOT_USED;

  @JsonProperty
  private String nativeQuerySqlPlanningMode = NATIVE_QUERY_SQL_PLANNING_MODE_COUPLED; // can be COUPLED or DECOUPLED
  public static final String NATIVE_QUERY_SQL_PLANNING_MODE_COUPLED = "COUPLED";
  public static final String NATIVE_QUERY_SQL_PLANNING_MODE_DECOUPLED = "DECOUPLED";

  private boolean serializeComplexValues = true;

  public int getMaxNumericInFilters()
  {
    return maxNumericInFilters;
  }

  public int getMaxTopNLimit()
  {
    return maxTopNLimit;
  }

  public boolean isUseApproximateCountDistinct()
  {
    return useApproximateCountDistinct;
  }

  public boolean isUseGroupingSetForExactDistinct()
  {
    return useGroupingSetForExactDistinct;
  }

  public boolean isUseApproximateTopN()
  {
    return useApproximateTopN;
  }

  public boolean isRequireTimeCondition()
  {
    return requireTimeCondition;
  }

  public DateTimeZone getSqlTimeZone()
  {
    return sqlTimeZone;
  }

  public boolean shouldSerializeComplexValues()
  {
    return serializeComplexValues;
  }

  public boolean isComputeInnerJoinCostAsFilter()
  {
    return computeInnerJoinCostAsFilter;
  }

  public boolean isAuthorizeSystemTablesDirectly()
  {
    return authorizeSystemTablesDirectly;
  }

  public boolean isUseNativeQueryExplain()
  {
    return useNativeQueryExplain;
  }

  /**
   * @return true if special virtual columns should not be optimized and should
   * always be of type "expressions", false otherwise.
   */
  public boolean isForceExpressionVirtualColumns()
  {
    return forceExpressionVirtualColumns;
  }

  public String getNativeQuerySqlPlanningMode()
  {
    return nativeQuerySqlPlanningMode;
  }

  public PlannerConfig withOverrides(final Map queryContext)
  {
    if (queryContext.isEmpty()) {
      return this;
    }
    return toBuilder()
        .withOverrides(queryContext)
        .build();
  }

  @Override
  public boolean equals(final Object o)
  {
    if (this == o) {
      return true;
    }
    if (o == null || getClass() != o.getClass()) {
      return false;
    }
    final PlannerConfig that = (PlannerConfig) o;
    return maxTopNLimit == that.maxTopNLimit &&
           useApproximateCountDistinct == that.useApproximateCountDistinct &&
           useApproximateTopN == that.useApproximateTopN &&
           requireTimeCondition == that.requireTimeCondition &&
           serializeComplexValues == that.serializeComplexValues &&
           Objects.equals(sqlTimeZone, that.sqlTimeZone) &&
           useNativeQueryExplain == that.useNativeQueryExplain &&
           forceExpressionVirtualColumns == that.forceExpressionVirtualColumns &&
           useGroupingSetForExactDistinct == that.useGroupingSetForExactDistinct &&
           computeInnerJoinCostAsFilter == that.computeInnerJoinCostAsFilter &&
           authorizeSystemTablesDirectly == that.authorizeSystemTablesDirectly &&
           maxNumericInFilters == that.maxNumericInFilters &&
           nativeQuerySqlPlanningMode.equals(that.nativeQuerySqlPlanningMode);
  }

  @Override
  public int hashCode()
  {

    return Objects.hash(
        maxTopNLimit,
        useApproximateCountDistinct,
        useApproximateTopN,
        requireTimeCondition,
        sqlTimeZone,
        serializeComplexValues,
        useNativeQueryExplain,
        forceExpressionVirtualColumns,
        nativeQuerySqlPlanningMode
    );
  }

  @Override
  public String toString()
  {
    return "PlannerConfig{" +
           "maxTopNLimit=" + maxTopNLimit +
           ", useApproximateCountDistinct=" + useApproximateCountDistinct +
           ", useApproximateTopN=" + useApproximateTopN +
           ", requireTimeCondition=" + requireTimeCondition +
           ", sqlTimeZone=" + sqlTimeZone +
           ", serializeComplexValues=" + serializeComplexValues +
           ", useNativeQueryExplain=" + useNativeQueryExplain +
           ", nativeQuerySqlPlanningMode=" + nativeQuerySqlPlanningMode +
           '}';
  }

  public static Builder builder()
  {
    return new PlannerConfig().toBuilder();
  }

  public Builder toBuilder()
  {
    return new Builder(this);
  }

  /**
   * Builder for {@link PlannerConfig}, primarily for use in tests to
   * allow setting options programmatically rather than from the command
   * line or a properties file. Starts with values from an existing
   * (typically default) config.
   */
  public static class Builder
  {
    private int maxTopNLimit;
    private boolean useApproximateCountDistinct;
    private boolean useApproximateTopN;
    private boolean requireTimeCondition;
    private DateTimeZone sqlTimeZone;
    private boolean useGroupingSetForExactDistinct;
    private boolean computeInnerJoinCostAsFilter;
    private boolean authorizeSystemTablesDirectly;
    private boolean useNativeQueryExplain;
    private boolean forceExpressionVirtualColumns;
    private int maxNumericInFilters;
    private boolean serializeComplexValues;
    private String nativeQuerySqlPlanningMode;

    public Builder(PlannerConfig base)
    {
      // Note: use accessors, not fields, since some tests change the
      // config by defining a subclass.

      maxTopNLimit = base.getMaxTopNLimit();
      useApproximateCountDistinct = base.isUseApproximateCountDistinct();
      useApproximateTopN = base.isUseApproximateTopN();
      requireTimeCondition = base.isRequireTimeCondition();
      sqlTimeZone = base.getSqlTimeZone();
      useGroupingSetForExactDistinct = base.isUseGroupingSetForExactDistinct();
      computeInnerJoinCostAsFilter = base.computeInnerJoinCostAsFilter;
      authorizeSystemTablesDirectly = base.isAuthorizeSystemTablesDirectly();
      useNativeQueryExplain = base.isUseNativeQueryExplain();
      forceExpressionVirtualColumns = base.isForceExpressionVirtualColumns();
      maxNumericInFilters = base.getMaxNumericInFilters();
      serializeComplexValues = base.shouldSerializeComplexValues();
      nativeQuerySqlPlanningMode = base.getNativeQuerySqlPlanningMode();
    }

    public Builder requireTimeCondition(boolean option)
    {
      this.requireTimeCondition = option;
      return this;
    }

    public Builder maxTopNLimit(int value)
    {
      this.maxTopNLimit = value;
      return this;
    }

    public Builder maxNumericInFilters(int value)
    {
      this.maxNumericInFilters = value;
      return this;
    }

    public Builder useApproximateCountDistinct(boolean option)
    {
      this.useApproximateCountDistinct = option;
      return this;
    }

    public Builder useApproximateTopN(boolean option)
    {
      this.useApproximateTopN = option;
      return this;
    }

    public Builder useGroupingSetForExactDistinct(boolean option)
    {
      this.useGroupingSetForExactDistinct = option;
      return this;
    }

    public Builder computeInnerJoinCostAsFilter(boolean option)
    {
      this.computeInnerJoinCostAsFilter = option;
      return this;
    }

    public Builder sqlTimeZone(DateTimeZone value)
    {
      this.sqlTimeZone = value;
      return this;
    }

    public Builder authorizeSystemTablesDirectly(boolean option)
    {
      this.authorizeSystemTablesDirectly = option;
      return this;
    }

    public Builder serializeComplexValues(boolean option)
    {
      this.serializeComplexValues = option;
      return this;
    }

    public Builder useNativeQueryExplain(boolean option)
    {
      this.useNativeQueryExplain = option;
      return this;
    }

    public Builder nativeQuerySqlPlanningMode(String mode)
    {
      this.nativeQuerySqlPlanningMode = mode;
      return this;
    }

    public Builder withOverrides(final Map queryContext)
    {
      useApproximateCountDistinct = QueryContexts.parseBoolean(
          queryContext,
          CTX_KEY_USE_APPROXIMATE_COUNT_DISTINCT,
          useApproximateCountDistinct
      );
      useGroupingSetForExactDistinct = QueryContexts.parseBoolean(
          queryContext,
          CTX_KEY_USE_GROUPING_SET_FOR_EXACT_DISTINCT,
          useGroupingSetForExactDistinct
      );
      useApproximateTopN = QueryContexts.parseBoolean(
          queryContext,
          CTX_KEY_USE_APPROXIMATE_TOPN,
          useApproximateTopN
      );
      computeInnerJoinCostAsFilter = QueryContexts.parseBoolean(
          queryContext,
          CTX_COMPUTE_INNER_JOIN_COST_AS_FILTER,
          computeInnerJoinCostAsFilter
      );
      useNativeQueryExplain = QueryContexts.parseBoolean(
          queryContext,
          CTX_KEY_USE_NATIVE_QUERY_EXPLAIN,
          useNativeQueryExplain
      );
      forceExpressionVirtualColumns = QueryContexts.parseBoolean(
          queryContext,
          CTX_KEY_FORCE_EXPRESSION_VIRTUAL_COLUMNS,
          forceExpressionVirtualColumns
      );
      final int queryContextMaxNumericInFilters = QueryContexts.parseInt(
          queryContext,
          CTX_MAX_NUMERIC_IN_FILTERS,
          maxNumericInFilters
      );
      maxNumericInFilters = validateMaxNumericInFilters(
          queryContextMaxNumericInFilters,
          maxNumericInFilters);
      nativeQuerySqlPlanningMode = QueryContexts.parseString(
          queryContext,
          CTX_NATIVE_QUERY_SQL_PLANNING_MODE,
          nativeQuerySqlPlanningMode
      );
      return this;
    }

    private static int validateMaxNumericInFilters(int queryContextMaxNumericInFilters, int systemConfigMaxNumericInFilters)
    {
      // if maxNumericInFIlters through context == 0 catch exception
      // else if query context exceeds system set value throw error
      if (queryContextMaxNumericInFilters == 0) {
        throw new UOE("[%s] must be greater than 0", CTX_MAX_NUMERIC_IN_FILTERS);
      } else if (queryContextMaxNumericInFilters > systemConfigMaxNumericInFilters
                 && systemConfigMaxNumericInFilters != NUM_FILTER_NOT_USED) {
        throw new UOE(
            "Expected parameter[%s] cannot exceed system set value of [%d]",
            CTX_MAX_NUMERIC_IN_FILTERS,
            systemConfigMaxNumericInFilters
        );
      }
      // if system set value is not present, thereby inferring default of -1
      if (systemConfigMaxNumericInFilters == NUM_FILTER_NOT_USED) {
        return systemConfigMaxNumericInFilters;
      }
      // all other cases return the valid query context value
      return queryContextMaxNumericInFilters;
    }

    public PlannerConfig build()
    {
      PlannerConfig config = new PlannerConfig();
      config.maxTopNLimit = maxTopNLimit;
      config.useApproximateCountDistinct = useApproximateCountDistinct;
      config.useApproximateTopN = useApproximateTopN;
      config.requireTimeCondition = requireTimeCondition;
      config.sqlTimeZone = sqlTimeZone;
      config.useGroupingSetForExactDistinct = useGroupingSetForExactDistinct;
      config.computeInnerJoinCostAsFilter = computeInnerJoinCostAsFilter;
      config.authorizeSystemTablesDirectly = authorizeSystemTablesDirectly;
      config.useNativeQueryExplain = useNativeQueryExplain;
      config.maxNumericInFilters = maxNumericInFilters;
      config.forceExpressionVirtualColumns = forceExpressionVirtualColumns;
      config.serializeComplexValues = serializeComplexValues;
      config.nativeQuerySqlPlanningMode = nativeQuerySqlPlanningMode;
      return config;
    }
  }
}




© 2015 - 2025 Weber Informatics LLC | Privacy Policy