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

org.apache.druid.sql.guice.SqlModule 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 org.apache.druid.sql.guice;

import com.google.common.base.Preconditions;
import com.google.common.base.Supplier;
import com.google.inject.Binder;
import com.google.inject.Inject;
import com.google.inject.Key;
import com.google.inject.Module;
import com.google.inject.Provides;
import org.apache.druid.catalog.model.TableDefnRegistry;
import org.apache.druid.guice.LazySingleton;
import org.apache.druid.guice.PolyBind;
import org.apache.druid.guice.annotations.NativeQuery;
import org.apache.druid.java.util.emitter.service.ServiceEmitter;
import org.apache.druid.query.DefaultQueryConfig;
import org.apache.druid.server.QueryScheduler;
import org.apache.druid.server.log.RequestLogger;
import org.apache.druid.sql.SqlLifecycleManager;
import org.apache.druid.sql.SqlStatementFactory;
import org.apache.druid.sql.SqlToolbox;
import org.apache.druid.sql.avatica.AvaticaModule;
import org.apache.druid.sql.calcite.aggregation.SqlAggregationModule;
import org.apache.druid.sql.calcite.expression.builtin.QueryLookupOperatorConversion;
import org.apache.druid.sql.calcite.planner.CalcitePlannerModule;
import org.apache.druid.sql.calcite.planner.CatalogResolver;
import org.apache.druid.sql.calcite.planner.PlannerFactory;
import org.apache.druid.sql.calcite.run.NativeSqlEngine;
import org.apache.druid.sql.calcite.schema.DruidCalciteSchemaModule;
import org.apache.druid.sql.calcite.schema.DruidSchemaManager;
import org.apache.druid.sql.calcite.schema.NoopDruidSchemaManager;
import org.apache.druid.sql.calcite.view.DruidViewModule;
import org.apache.druid.sql.calcite.view.NoopViewManager;
import org.apache.druid.sql.calcite.view.ViewManager;
import org.apache.druid.sql.http.SqlHttpModule;

import java.util.Properties;

public class SqlModule implements Module
{
  public static final String PROPERTY_SQL_ENABLE = "druid.sql.enable";
  public static final String PROPERTY_SQL_ENABLE_JSON_OVER_HTTP = "druid.sql.http.enable";
  public static final String PROPERTY_SQL_ENABLE_AVATICA = "druid.sql.avatica.enable";
  public static final String PROPERTY_SQL_VIEW_MANAGER_TYPE = "druid.sql.viewmanager.type";
  public static final String PROPERTY_SQL_SCHEMA_MANAGER_TYPE = "druid.sql.schemamanager.type";
  public static final String PROPERTY_SQL_APPROX_COUNT_DISTINCT_CHOICE = "druid.sql.approxCountDistinct.function";

  private Properties props;

  @Inject
  public void setProps(Properties props)
  {
    this.props = props;
  }

  @Override
  public void configure(Binder binder)
  {
    if (!isEnabled()) {
      return;
    }

    PolyBind.optionBinder(binder, Key.get(ViewManager.class))
            .addBinding(NoopViewManager.TYPE)
            .to(NoopViewManager.class)
            .in(LazySingleton.class);

    PolyBind.createChoiceWithDefault(
        binder,
        PROPERTY_SQL_VIEW_MANAGER_TYPE,
        Key.get(ViewManager.class),
        NoopViewManager.TYPE
    );

    PolyBind.optionBinder(binder, Key.get(DruidSchemaManager.class))
            .addBinding(NoopDruidSchemaManager.TYPE)
            .to(NoopDruidSchemaManager.class)
            .in(LazySingleton.class);

    PolyBind.createChoiceWithDefault(
        binder,
        PROPERTY_SQL_SCHEMA_MANAGER_TYPE,
        Key.get(DruidSchemaManager.class),
        NoopDruidSchemaManager.TYPE
    );

    binder.bind(TableDefnRegistry.class).in(LazySingleton.class);

    binder.install(new DruidCalciteSchemaModule());
    binder.install(new CalcitePlannerModule());
    binder.install(new SqlAggregationModule());
    binder.install(new DruidViewModule());

    binder.install(new SqlStatementFactoryModule());

    // QueryLookupOperatorConversion isn't in DruidOperatorTable since it needs a LookupExtractorFactoryContainerProvider injected.
    SqlBindings.addOperatorConversion(binder, QueryLookupOperatorConversion.class);

    if (isJsonOverHttpEnabled()) {
      binder.install(new SqlHttpModule());
    }

    if (isAvaticaEnabled()) {
      binder.install(new AvaticaModule());
    }

    // Default do-nothing catalog resolver
    binder.bind(CatalogResolver.class).toInstance(CatalogResolver.NULL_RESOLVER);
  }

  private boolean isEnabled()
  {
    Preconditions.checkNotNull(props, "props");
    return Boolean.valueOf(props.getProperty(PROPERTY_SQL_ENABLE, "true"));
  }

  private boolean isJsonOverHttpEnabled()
  {
    Preconditions.checkNotNull(props, "props");
    return Boolean.valueOf(props.getProperty(PROPERTY_SQL_ENABLE_JSON_OVER_HTTP, "true"));
  }

  private boolean isAvaticaEnabled()
  {
    Preconditions.checkNotNull(props, "props");
    return Boolean.valueOf(props.getProperty(PROPERTY_SQL_ENABLE_AVATICA, "true"));
  }

  /**
   * We create a new class for this module so that it can be shared by tests.  The structuring of the SqlModule
   * at time of writing was not conducive to reuse in test code, so, instead of fixing that we just take the easy
   * way out of adding the test-reusable code to this module and reuse that.
   *
   * Generally speaking, the injection pattern done by this module is a bit circuitous.  The `SqlToolbox` acts as
   * if it can be injected with all of its dependencies, but also expects to be mutated with a new SqlEngine.  We
   * should likely look at adjusting the object dependencies to actually depend on the SqlToolbox and create
   * different Toolboxes for the different way that queries are done.  But, for now, I'm not changing the interfaces.
   */
  public static class SqlStatementFactoryModule implements Module
  {

    @Provides
    @LazySingleton
    public SqlToolbox makeSqlToolbox(
        final PlannerFactory plannerFactory,
        final ServiceEmitter emitter,
        final RequestLogger requestLogger,
        final QueryScheduler queryScheduler,
        final Supplier defaultQueryConfig,
        final SqlLifecycleManager sqlLifecycleManager
    )
    {
      return new SqlToolbox(
          null,
          plannerFactory,
          emitter,
          requestLogger,
          queryScheduler,
          defaultQueryConfig.get(),
          sqlLifecycleManager
      );
    }

    @Provides
    @NativeQuery
    @LazySingleton
    public SqlStatementFactory makeNativeSqlStatementFactory(
        final NativeSqlEngine sqlEngine,
        SqlToolbox toolbox
    )
    {
      return new SqlStatementFactory(toolbox.withEngine(sqlEngine));
    }

    @Override
    public void configure(Binder binder)
    {
      // Do nothing, this class exists for the Provider methods
    }
  }
}




© 2015 - 2025 Weber Informatics LLC | Privacy Policy