paul-rogers commented on code in PR #13766:
URL: https://github.com/apache/druid/pull/13766#discussion_r1099279211


##########
docs/configuration/index.md:
##########
@@ -1919,6 +1919,7 @@ The Druid SQL server is configured through the following 
properties on the Broke
 |`druid.sql.planner.authorizeSystemTablesDirectly`|If true, Druid authorizes 
queries against any of the system schema tables (`sys` in SQL) as 
`SYSTEM_TABLE` resources which require `READ` access, in addition to 
permissions based content filtering.|false|
 |`druid.sql.planner.useNativeQueryExplain`|If true, `EXPLAIN PLAN FOR` will 
return the explain plan as a JSON representation of equivalent native query(s), 
else it will return the original version of explain plan generated by Calcite. 
It can be overridden per query with `useNativeQueryExplain` context key.|true|
 |`druid.sql.planner.maxNumericInFilters`|Max limit for the amount of numeric 
values that can be compared for a string type dimension when the entire SQL 
WHERE clause of a query translates to an [OR](../querying/filters.md#or) of 
[Bound filter](../querying/filters.md#bound-filter). By default, Druid does not 
restrict the amount of numeric Bound Filters on String columns, although this 
situation may block other queries from running. Set this property to a smaller 
value to prevent Druid from running queries that have prohibitively long 
segment processing times. The optimal limit requires some trial and error; we 
recommend starting with 100.  Users who submit a query that exceeds the limit 
of `maxNumericInFilters` should instead rewrite their queries to use strings in 
the `WHERE` clause instead of numbers. For example, `WHERE someString IN 
(‘123’, ‘456’)`. If this value is disabled, `maxNumericInFilters` set through 
query context is ignored.|`-1` (disabled)|
+|`druid.sql.planner.operatorConversion.denyList`| The list of operator 
conversions that should be denied.|`[]` (no operator conversions are 
disallowed)|

Review Comment:
   The `operatorConversion` is an obscure Druid concept. For users, it should 
just be `operator`.



##########
sql/src/main/java/org/apache/druid/sql/calcite/planner/DruidOperatorTable.java:
##########
@@ -437,6 +442,12 @@ public DruidOperatorTable(
 
     for (SqlOperatorConversion operatorConversion : operatorConversions) {
       final OperatorKey operatorKey = 
OperatorKey.of(operatorConversion.calciteOperator());
+      if (operationConversionDenySet.contains(operatorKey.name)) {
+        LOG.info(
+            "operatorKey '%s' not being added to available operatorConversions 
as it was found in deny list.",

Review Comment:
   Perhaps:
   
   ```
   Operator %s is not available as it is in the deny list.
   ```



##########
sql/src/main/java/org/apache/druid/sql/calcite/planner/PlannerOperatorConversionConfig.java:
##########
@@ -0,0 +1,108 @@
+/*
+ * 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 com.google.common.collect.ImmutableList;
+
+import java.util.List;
+import java.util.Objects;
+
+public class PlannerOperatorConversionConfig
+{
+  private static final List<String> DEFAULT_DENY_LIST = ImmutableList.of();
+  @JsonProperty
+  private List<String> denyList = DEFAULT_DENY_LIST;
+
+
+  public List<String> getDenyList()
+  {
+    return denyList;

Review Comment:
   I wonder, can this cause an NPE? The code handles the case in which the user 
never sets the property. But what if they do this:
   
   ```
   druid...denyList=null
   ```
   
   Should we play it safe? Omit the default value and just do:
   
   ```java
      return denyList == null ? ImmutableList.of() : denyList;
   ```
   
   Seems OK since the method will only ever be called once, when building the 
operator table.



##########
sql/src/main/java/org/apache/druid/sql/calcite/planner/CalcitePlannerModule.java:
##########
@@ -40,6 +40,7 @@ public void configure(Binder binder)
     // We're actually binding the class to the config prefix, not the other 
way around.
     JsonConfigProvider.bind(binder, "druid.sql.planner", PlannerConfig.class);
     JsonConfigProvider.bind(binder, "druid.sql.planner", 
SegmentMetadataCacheConfig.class);
+    JsonConfigProvider.bind(binder, "druid.sql.planner.operatorConversion", 
PlannerOperatorConversionConfig.class);

Review Comment:
   It is very useful to put the path in the config object:
   
   ```java
   class PlannerOperatorConversionConfig {
     public static final String CONFIG_PATH = 
"druid.sql.planner.operatorConversion";
   ```
   
   And:
   
   ```java
   JsonConfigProvider.bind(binder, PlannerOperatorConversionConfig.CONFIG_PATH, 
PlannerOperatorConversionConfig.class);
   ```



##########
sql/src/test/java/org/apache/druid/sql/calcite/planner/DruidOperatorTableTest.java:
##########
@@ -0,0 +1,129 @@
+/*
+ * 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.databind.ObjectMapper;
+import com.google.common.collect.ImmutableList;
+import com.google.common.collect.ImmutableSet;
+import org.apache.druid.catalog.model.TableDefnRegistry;
+import org.apache.druid.catalog.model.table.TableFunction;
+import org.apache.druid.sql.calcite.expression.SqlOperatorConversion;
+import org.apache.druid.sql.calcite.external.ExternalOperatorConversion;
+import org.apache.druid.sql.calcite.external.HttpOperatorConversion;
+import org.apache.druid.sql.calcite.external.InlineOperatorConversion;
+import org.apache.druid.sql.calcite.external.LocalOperatorConversion;
+import org.junit.Assert;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.mockito.Answers;
+import org.mockito.Mock;
+import org.mockito.Mockito;
+import org.mockito.junit.MockitoJUnitRunner;
+
+import static org.mockito.ArgumentMatchers.any;
+
+@RunWith(MockitoJUnitRunner.class)
+public class DruidOperatorTableTest
+{
+  private static final ObjectMapper OBJECT_MAPPER = new ObjectMapper();
+
+  @Mock (answer = Answers.RETURNS_DEEP_STUBS) TableDefnRegistry 
tableDefnRegistry;
+
+  @Mock TableFunction tableFunction;
+  private DruidOperatorTable operatorTable;
+
+  @Test
+  public void 
test_operatorTable_with_operatorConversionDenyList_deniedConversionsUnavailable()
+  {
+    Mockito.when(tableDefnRegistry.jsonMapper()).thenReturn(OBJECT_MAPPER);

Review Comment:
   This test is overkill. First, there should be no need for mocks. If we want 
to use the actual extern operators, just create a real instance.
   
   ```
     protected final TableDefnRegistry tableRegistry;
     ...
       this.tableRegistry = new TableDefnRegistry(jsonMapper);
   ```
   
   Mocks are evil: they will cause this test to silently fail if we change the 
`TableDefnRegistry` class. Direct use will trigger compile errors if we change 
the interface.
   
   Second, there is no need to actually use these functions. The deny list is 
generic: you can use _any_ built in function. Just register as done in other 
tests, then resolve.
   
   In fact, you can create a `Calcite<Something>Test` so this can be tested via 
an actual query, if you wanted. Else, just look how those tests set up the 
operator registry.



##########
sql/src/main/java/org/apache/druid/sql/calcite/planner/PlannerOperatorConversionConfig.java:
##########
@@ -0,0 +1,108 @@
+/*
+ * 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 com.google.common.collect.ImmutableList;
+
+import java.util.List;
+import java.util.Objects;
+
+public class PlannerOperatorConversionConfig

Review Comment:
   Please rename to `PlannerOperatorConfig`. Again, the "conversion" bit is 
just a bit of internal plumbing.



##########
sql/src/main/java/org/apache/druid/sql/calcite/planner/PlannerOperatorConversionConfig.java:
##########
@@ -0,0 +1,108 @@
+/*
+ * 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 com.google.common.collect.ImmutableList;
+
+import java.util.List;
+import java.util.Objects;
+
+public class PlannerOperatorConversionConfig
+{
+  private static final List<String> DEFAULT_DENY_LIST = ImmutableList.of();
+  @JsonProperty
+  private List<String> denyList = DEFAULT_DENY_LIST;
+
+
+  public List<String> getDenyList()
+  {
+    return denyList;
+  }
+
+  @Override
+  public boolean equals(final Object o)
+  {
+    if (this == o) {
+      return true;
+    }
+    if (o == null || getClass() != o.getClass()) {
+      return false;
+    }
+    final PlannerOperatorConversionConfig that = 
(PlannerOperatorConversionConfig) o;
+    return denyList.equals(that.denyList);
+  }
+
+  @Override
+  public int hashCode()
+  {
+
+    return Objects.hash(
+        denyList
+    );
+  }
+
+  @Override
+  public String toString()
+  {
+    return "PlannerOperatorConversionConfig{" +
+           "denyList=" + denyList +
+           '}';
+  }
+
+  public static Builder builder()

Review Comment:
   This class is so simple it doesn't need a builder. Instead, create a static 
method:
   
   ```
   public static newInstance(List<String> denyList)
   {
     PlannerOperatorConversionConfig config = new 
PlannerOperatorConversionConfig();
     config.denyList = denyList;
     return config;
   }
   ```



##########
sql/src/main/java/org/apache/druid/sql/calcite/planner/PlannerOperatorConversionConfig.java:
##########
@@ -0,0 +1,108 @@
+/*
+ * 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 com.google.common.collect.ImmutableList;
+
+import java.util.List;
+import java.util.Objects;
+
+public class PlannerOperatorConversionConfig
+{
+  private static final List<String> DEFAULT_DENY_LIST = ImmutableList.of();
+  @JsonProperty
+  private List<String> denyList = DEFAULT_DENY_LIST;
+
+
+  public List<String> getDenyList()
+  {
+    return denyList;
+  }
+
+  @Override
+  public boolean equals(final Object o)
+  {
+    if (this == o) {
+      return true;
+    }
+    if (o == null || getClass() != o.getClass()) {
+      return false;
+    }
+    final PlannerOperatorConversionConfig that = 
(PlannerOperatorConversionConfig) o;
+    return denyList.equals(that.denyList);
+  }
+
+  @Override
+  public int hashCode()
+  {
+
+    return Objects.hash(
+        denyList
+    );

Review Comment:
   Nit: remove blank line. Call to `hash()` can be all on one line.



##########
sql/src/main/java/org/apache/druid/sql/calcite/planner/PlannerOperatorConversionConfig.java:
##########
@@ -0,0 +1,108 @@
+/*
+ * 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 com.google.common.collect.ImmutableList;
+
+import java.util.List;
+import java.util.Objects;
+
+public class PlannerOperatorConversionConfig
+{
+  private static final List<String> DEFAULT_DENY_LIST = ImmutableList.of();
+  @JsonProperty
+  private List<String> denyList = DEFAULT_DENY_LIST;

Review Comment:
   Nit: No real need to define a constant. Only one instance of this class will 
ever exist per Druid server. Except in tests.



-- 
This is an automated message from the Apache Git Service.
To respond to the message, please log on to GitHub and use the
URL above to go to the specific comment.

To unsubscribe, e-mail: [email protected]

For queries about this service, please contact Infrastructure at:
[email protected]


---------------------------------------------------------------------
To unsubscribe, e-mail: [email protected]
For additional commands, e-mail: [email protected]

Reply via email to