zabetak commented on a change in pull request #2482:
URL: https://github.com/apache/calcite/pull/2482#discussion_r748152813



##########
File path: core/src/main/java/org/apache/calcite/rel/rel2sql/SqlImplementor.java
##########
@@ -161,8 +164,20 @@ protected SqlImplementor(SqlDialect dialect) {
 
   /** Visits a relational expression that has no parent. */
   public final Result visitRoot(RelNode r) {

Review comment:
       This is indeed a good place to place the logic for the "GroupByConstant" 
rewritting. Have you verified that everybody or (at least the significant) 
callers are passing from here? It seems for instance that 
`JdbcToEnumerableConverter` does not use this method.

##########
File path: 
core/src/main/java/org/apache/calcite/rel/rules/GroupByConstantAddJoinRule.java
##########
@@ -0,0 +1,196 @@
+/*
+ * 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.calcite.rel.rules;
+
+import org.apache.calcite.plan.RelOptRuleCall;
+import org.apache.calcite.plan.RelRule;
+import org.apache.calcite.rel.core.Aggregate;
+import org.apache.calcite.rel.core.Join;
+import org.apache.calcite.rel.core.JoinRelType;
+import org.apache.calcite.rel.core.Project;
+import org.apache.calcite.rel.logical.LogicalAggregate;
+import org.apache.calcite.rel.logical.LogicalProject;
+import org.apache.calcite.rel.logical.LogicalValues;
+import org.apache.calcite.rel.type.RelDataTypeFactory;
+import org.apache.calcite.rex.RexBuilder;
+import org.apache.calcite.rex.RexInputRef;
+import org.apache.calcite.rex.RexLiteral;
+import org.apache.calcite.rex.RexNode;
+import org.apache.calcite.sql.type.SqlTypeFamily;
+import org.apache.calcite.sql.type.SqlTypeName;
+import org.apache.calcite.tools.RelBuilder;
+import org.apache.calcite.util.Pair;
+
+import com.google.common.collect.ImmutableList;
+
+import org.immutables.value.Value;
+
+import java.util.ArrayList;
+import java.util.List;
+
+/**
+ * Planner rule that recognizes  a {@link 
org.apache.calcite.rel.core.Aggregate}
+ * on top of a {@link org.apache.calcite.rel.core.Project} where the 
aggregate's group set
+ * contains boolean literals (true, false), and removes the literals from the 
group keys by joining
+ * with a dummy table of boolean literals.
+ *
+ * select avg(sal)
+ * from emp
+ * group by true;
+ *
+ * becomes
+ *
+ * select avg(sal)
+ * from emp, (select true x) dummy
+ * group by dummy.x;
+ */
[email protected]
+public class GroupByConstantAddJoinRule
+    extends RelRule<GroupByConstantAddJoinRule.Config> {
+
+  /**
+   * Creates a RelRule.
+   *
+   */
+  protected GroupByConstantAddJoinRule(Config config) {
+    super(config);
+  }
+
+  @Override public boolean matches(RelOptRuleCall call) {
+    final Aggregate aggregate = call.rel(0);
+    final Project project = call.rel(1);
+
+    // avoids transforming correlated queries
+    if (aggregate.getAggCallList().size() == 0) {
+      return false;
+    }
+
+    boolean hasBooleanLiteral = false;
+
+    for (int groupKey: aggregate.getGroupSet().asList()) {
+      if (groupKey >= aggregate.getRowType().getFieldCount()) {
+        continue;
+      }
+      hasBooleanLiteral = 
(aggregate.getRowType().getFieldList().get(groupKey).getType().getFamily()
+          == SqlTypeFamily.BOOLEAN)
+          && (project.getProjects().get(groupKey) instanceof RexLiteral);

Review comment:
       Why do you need to work with rowType of the `Aggregate`? Can't you 
simply rely on `project.getProjects().get(groupKey)` and check the type of the 
`RexLiteral`?

##########
File path: 
core/src/main/java/org/apache/calcite/rel/rules/GroupByConstantAddJoinRule.java
##########
@@ -0,0 +1,196 @@
+/*
+ * 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.calcite.rel.rules;
+
+import org.apache.calcite.plan.RelOptRuleCall;
+import org.apache.calcite.plan.RelRule;
+import org.apache.calcite.rel.core.Aggregate;
+import org.apache.calcite.rel.core.Join;
+import org.apache.calcite.rel.core.JoinRelType;
+import org.apache.calcite.rel.core.Project;
+import org.apache.calcite.rel.logical.LogicalAggregate;
+import org.apache.calcite.rel.logical.LogicalProject;
+import org.apache.calcite.rel.logical.LogicalValues;
+import org.apache.calcite.rel.type.RelDataTypeFactory;
+import org.apache.calcite.rex.RexBuilder;
+import org.apache.calcite.rex.RexInputRef;
+import org.apache.calcite.rex.RexLiteral;
+import org.apache.calcite.rex.RexNode;
+import org.apache.calcite.sql.type.SqlTypeFamily;
+import org.apache.calcite.sql.type.SqlTypeName;
+import org.apache.calcite.tools.RelBuilder;
+import org.apache.calcite.util.Pair;
+
+import com.google.common.collect.ImmutableList;
+
+import org.immutables.value.Value;
+
+import java.util.ArrayList;
+import java.util.List;
+
+/**
+ * Planner rule that recognizes  a {@link 
org.apache.calcite.rel.core.Aggregate}
+ * on top of a {@link org.apache.calcite.rel.core.Project} where the 
aggregate's group set
+ * contains boolean literals (true, false), and removes the literals from the 
group keys by joining
+ * with a dummy table of boolean literals.
+ *
+ * select avg(sal)
+ * from emp
+ * group by true;
+ *
+ * becomes
+ *
+ * select avg(sal)
+ * from emp, (select true x) dummy
+ * group by dummy.x;
+ */
[email protected]
+public class GroupByConstantAddJoinRule

Review comment:
       Usually rules are named after relational expressions (`RelNode`) and not 
SQL clauses. Some potentially better names: 
   - `AggregateProjectConstantToColumnReferenceRule`
   - `AggregateProjectConstantToDummyJoinRule`
   - `AggregateProjectConstantToDummyReferenceRule`
   
   Any of the above is fine with me.

##########
File path: 
core/src/main/java/org/apache/calcite/rel/rules/GroupByConstantAddJoinRule.java
##########
@@ -0,0 +1,196 @@
+/*
+ * 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.calcite.rel.rules;
+
+import org.apache.calcite.plan.RelOptRuleCall;
+import org.apache.calcite.plan.RelRule;
+import org.apache.calcite.rel.core.Aggregate;
+import org.apache.calcite.rel.core.Join;
+import org.apache.calcite.rel.core.JoinRelType;
+import org.apache.calcite.rel.core.Project;
+import org.apache.calcite.rel.logical.LogicalAggregate;
+import org.apache.calcite.rel.logical.LogicalProject;
+import org.apache.calcite.rel.logical.LogicalValues;
+import org.apache.calcite.rel.type.RelDataTypeFactory;
+import org.apache.calcite.rex.RexBuilder;
+import org.apache.calcite.rex.RexInputRef;
+import org.apache.calcite.rex.RexLiteral;
+import org.apache.calcite.rex.RexNode;
+import org.apache.calcite.sql.type.SqlTypeFamily;
+import org.apache.calcite.sql.type.SqlTypeName;
+import org.apache.calcite.tools.RelBuilder;
+import org.apache.calcite.util.Pair;
+
+import com.google.common.collect.ImmutableList;
+
+import org.immutables.value.Value;
+
+import java.util.ArrayList;
+import java.util.List;
+
+/**
+ * Planner rule that recognizes  a {@link 
org.apache.calcite.rel.core.Aggregate}
+ * on top of a {@link org.apache.calcite.rel.core.Project} where the 
aggregate's group set
+ * contains boolean literals (true, false), and removes the literals from the 
group keys by joining
+ * with a dummy table of boolean literals.
+ *
+ * select avg(sal)
+ * from emp
+ * group by true;
+ *
+ * becomes
+ *
+ * select avg(sal)
+ * from emp, (select true x) dummy
+ * group by dummy.x;
+ */
[email protected]
+public class GroupByConstantAddJoinRule
+    extends RelRule<GroupByConstantAddJoinRule.Config> {
+
+  /**
+   * Creates a RelRule.
+   *
+   */
+  protected GroupByConstantAddJoinRule(Config config) {
+    super(config);
+  }
+
+  @Override public boolean matches(RelOptRuleCall call) {
+    final Aggregate aggregate = call.rel(0);
+    final Project project = call.rel(1);
+
+    // avoids transforming correlated queries
+    if (aggregate.getAggCallList().size() == 0) {

Review comment:
       Why/How is the size of the call list related to correlated queries?

##########
File path: 
core/src/main/java/org/apache/calcite/rel/rules/GroupByConstantAddJoinRule.java
##########
@@ -0,0 +1,196 @@
+/*
+ * 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.calcite.rel.rules;
+
+import org.apache.calcite.plan.RelOptRuleCall;
+import org.apache.calcite.plan.RelRule;
+import org.apache.calcite.rel.core.Aggregate;
+import org.apache.calcite.rel.core.Join;
+import org.apache.calcite.rel.core.JoinRelType;
+import org.apache.calcite.rel.core.Project;
+import org.apache.calcite.rel.logical.LogicalAggregate;
+import org.apache.calcite.rel.logical.LogicalProject;
+import org.apache.calcite.rel.logical.LogicalValues;
+import org.apache.calcite.rel.type.RelDataTypeFactory;
+import org.apache.calcite.rex.RexBuilder;
+import org.apache.calcite.rex.RexInputRef;
+import org.apache.calcite.rex.RexLiteral;
+import org.apache.calcite.rex.RexNode;
+import org.apache.calcite.sql.type.SqlTypeFamily;
+import org.apache.calcite.sql.type.SqlTypeName;
+import org.apache.calcite.tools.RelBuilder;
+import org.apache.calcite.util.Pair;
+
+import com.google.common.collect.ImmutableList;
+
+import org.immutables.value.Value;
+
+import java.util.ArrayList;
+import java.util.List;
+
+/**
+ * Planner rule that recognizes  a {@link 
org.apache.calcite.rel.core.Aggregate}
+ * on top of a {@link org.apache.calcite.rel.core.Project} where the 
aggregate's group set
+ * contains boolean literals (true, false), and removes the literals from the 
group keys by joining
+ * with a dummy table of boolean literals.
+ *
+ * select avg(sal)
+ * from emp
+ * group by true;
+ *
+ * becomes
+ *
+ * select avg(sal)
+ * from emp, (select true x) dummy
+ * group by dummy.x;
+ */
[email protected]
+public class GroupByConstantAddJoinRule
+    extends RelRule<GroupByConstantAddJoinRule.Config> {
+
+  /**
+   * Creates a RelRule.
+   *
+   */
+  protected GroupByConstantAddJoinRule(Config config) {
+    super(config);
+  }
+
+  @Override public boolean matches(RelOptRuleCall call) {

Review comment:
       I suppose it is not safe to allow some subclass modify the `matches` 
method. Declare it final?

##########
File path: 
core/src/main/java/org/apache/calcite/rel/rules/GroupByConstantAddJoinRule.java
##########
@@ -0,0 +1,196 @@
+/*
+ * 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.calcite.rel.rules;
+
+import org.apache.calcite.plan.RelOptRuleCall;
+import org.apache.calcite.plan.RelRule;
+import org.apache.calcite.rel.core.Aggregate;
+import org.apache.calcite.rel.core.Join;
+import org.apache.calcite.rel.core.JoinRelType;
+import org.apache.calcite.rel.core.Project;
+import org.apache.calcite.rel.logical.LogicalAggregate;
+import org.apache.calcite.rel.logical.LogicalProject;
+import org.apache.calcite.rel.logical.LogicalValues;
+import org.apache.calcite.rel.type.RelDataTypeFactory;
+import org.apache.calcite.rex.RexBuilder;
+import org.apache.calcite.rex.RexInputRef;
+import org.apache.calcite.rex.RexLiteral;
+import org.apache.calcite.rex.RexNode;
+import org.apache.calcite.sql.type.SqlTypeFamily;
+import org.apache.calcite.sql.type.SqlTypeName;
+import org.apache.calcite.tools.RelBuilder;
+import org.apache.calcite.util.Pair;
+
+import com.google.common.collect.ImmutableList;
+
+import org.immutables.value.Value;
+
+import java.util.ArrayList;
+import java.util.List;
+
+/**
+ * Planner rule that recognizes  a {@link 
org.apache.calcite.rel.core.Aggregate}
+ * on top of a {@link org.apache.calcite.rel.core.Project} where the 
aggregate's group set
+ * contains boolean literals (true, false), and removes the literals from the 
group keys by joining
+ * with a dummy table of boolean literals.
+ *
+ * select avg(sal)
+ * from emp
+ * group by true;
+ *
+ * becomes
+ *
+ * select avg(sal)
+ * from emp, (select true x) dummy
+ * group by dummy.x;
+ */
[email protected]
+public class GroupByConstantAddJoinRule
+    extends RelRule<GroupByConstantAddJoinRule.Config> {
+
+  /**
+   * Creates a RelRule.
+   *
+   */
+  protected GroupByConstantAddJoinRule(Config config) {
+    super(config);
+  }
+
+  @Override public boolean matches(RelOptRuleCall call) {
+    final Aggregate aggregate = call.rel(0);
+    final Project project = call.rel(1);
+
+    // avoids transforming correlated queries
+    if (aggregate.getAggCallList().size() == 0) {
+      return false;
+    }
+
+    boolean hasBooleanLiteral = false;
+
+    for (int groupKey: aggregate.getGroupSet().asList()) {
+      if (groupKey >= aggregate.getRowType().getFieldCount()) {
+        continue;
+      }
+      hasBooleanLiteral = 
(aggregate.getRowType().getFieldList().get(groupKey).getType().getFamily()
+          == SqlTypeFamily.BOOLEAN)
+          && (project.getProjects().get(groupKey) instanceof RexLiteral);
+
+      if (hasBooleanLiteral) {
+        return true;
+      }
+    }
+
+    return hasBooleanLiteral;
+  }
+
+  private static LogicalValues getValues(
+      Aggregate aggregate, Project project, RexBuilder rexBuilder, RelBuilder 
builder) {
+    final ImmutableList.Builder<ImmutableList<RexLiteral>> tuples =
+        ImmutableList.builder();
+    ImmutableList.Builder<RexLiteral> b = ImmutableList.builder();
+    boolean truePresent = false;
+    boolean falsePresent = false;
+
+    for (int groupKey: aggregate.getGroupSet().asList()) {
+      if (project.getProjects().get(groupKey) instanceof RexLiteral) {
+        if (project.getProjects().get(groupKey).isAlwaysTrue()) {
+          truePresent = true;
+        }
+        if (project.getProjects().get(groupKey).isAlwaysFalse()) {
+          falsePresent = true;
+        }
+      }
+    }
+
+    RelDataTypeFactory.Builder relDataTypeBuilder = 
rexBuilder.getTypeFactory().builder();
+    if (truePresent) {
+      b.add(rexBuilder.makeLiteral(true));
+      relDataTypeBuilder.add("T", SqlTypeName.BOOLEAN);
+    }
+    if (falsePresent) {
+      b.add(rexBuilder.makeLiteral(false));
+      relDataTypeBuilder.add("F", SqlTypeName.BOOLEAN);
+    }
+    tuples.add(b.build());
+    LogicalValues values = LogicalValues.create(builder.getCluster(), 
relDataTypeBuilder.build(),
+        tuples.build());
+
+    return values;
+  }
+
+  @Override public void onMatch(RelOptRuleCall call) {
+    final Aggregate aggregate = call.rel(0);
+    final Project project = call.rel(1);
+
+    RelBuilder builder = call.builder();
+    RexBuilder rexBuilder = builder.getRexBuilder();
+
+    builder.push(project.getInput());
+    LogicalValues values = getValues(aggregate, project, rexBuilder, builder);
+    builder.push(values);
+
+    builder.join(JoinRelType.INNER, rexBuilder.makeLiteral(true));
+    Join join = (Join) builder.build();
+
+    List<RexNode> newProjects = new ArrayList<>();
+    List<String> names = new ArrayList<>();
+
+    int falseIndex = join.getRowType().getFieldCount() - 1;
+    int trueIndex = join.getRight().getRowType().getFieldCount() == 1
+        ? falseIndex : falseIndex - 1;
+    for (Pair<RexNode, String> pair: project.getNamedProjects()) {
+      if (pair.getKey() instanceof RexLiteral) {
+        if (pair.getKey().isAlwaysTrue()) {
+          newProjects.add(RexInputRef.of(trueIndex, join.getRowType()));

Review comment:
       ```suggestion
             newProjects.add(builder.field("T"));
   ```

##########
File path: 
core/src/main/java/org/apache/calcite/rel/rules/GroupByConstantAddJoinRule.java
##########
@@ -0,0 +1,196 @@
+/*
+ * 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.calcite.rel.rules;
+
+import org.apache.calcite.plan.RelOptRuleCall;
+import org.apache.calcite.plan.RelRule;
+import org.apache.calcite.rel.core.Aggregate;
+import org.apache.calcite.rel.core.Join;
+import org.apache.calcite.rel.core.JoinRelType;
+import org.apache.calcite.rel.core.Project;
+import org.apache.calcite.rel.logical.LogicalAggregate;
+import org.apache.calcite.rel.logical.LogicalProject;
+import org.apache.calcite.rel.logical.LogicalValues;
+import org.apache.calcite.rel.type.RelDataTypeFactory;
+import org.apache.calcite.rex.RexBuilder;
+import org.apache.calcite.rex.RexInputRef;
+import org.apache.calcite.rex.RexLiteral;
+import org.apache.calcite.rex.RexNode;
+import org.apache.calcite.sql.type.SqlTypeFamily;
+import org.apache.calcite.sql.type.SqlTypeName;
+import org.apache.calcite.tools.RelBuilder;
+import org.apache.calcite.util.Pair;
+
+import com.google.common.collect.ImmutableList;
+
+import org.immutables.value.Value;
+
+import java.util.ArrayList;
+import java.util.List;
+
+/**
+ * Planner rule that recognizes  a {@link 
org.apache.calcite.rel.core.Aggregate}
+ * on top of a {@link org.apache.calcite.rel.core.Project} where the 
aggregate's group set
+ * contains boolean literals (true, false), and removes the literals from the 
group keys by joining
+ * with a dummy table of boolean literals.
+ *
+ * select avg(sal)
+ * from emp
+ * group by true;
+ *
+ * becomes
+ *
+ * select avg(sal)
+ * from emp, (select true x) dummy
+ * group by dummy.x;
+ */
[email protected]
+public class GroupByConstantAddJoinRule
+    extends RelRule<GroupByConstantAddJoinRule.Config> {
+
+  /**
+   * Creates a RelRule.
+   *
+   */
+  protected GroupByConstantAddJoinRule(Config config) {
+    super(config);
+  }
+
+  @Override public boolean matches(RelOptRuleCall call) {
+    final Aggregate aggregate = call.rel(0);
+    final Project project = call.rel(1);
+
+    // avoids transforming correlated queries
+    if (aggregate.getAggCallList().size() == 0) {
+      return false;
+    }
+
+    boolean hasBooleanLiteral = false;
+
+    for (int groupKey: aggregate.getGroupSet().asList()) {
+      if (groupKey >= aggregate.getRowType().getFieldCount()) {
+        continue;
+      }
+      hasBooleanLiteral = 
(aggregate.getRowType().getFieldList().get(groupKey).getType().getFamily()
+          == SqlTypeFamily.BOOLEAN)
+          && (project.getProjects().get(groupKey) instanceof RexLiteral);
+
+      if (hasBooleanLiteral) {
+        return true;
+      }
+    }
+
+    return hasBooleanLiteral;
+  }
+
+  private static LogicalValues getValues(
+      Aggregate aggregate, Project project, RexBuilder rexBuilder, RelBuilder 
builder) {
+    final ImmutableList.Builder<ImmutableList<RexLiteral>> tuples =
+        ImmutableList.builder();
+    ImmutableList.Builder<RexLiteral> b = ImmutableList.builder();
+    boolean truePresent = false;
+    boolean falsePresent = false;
+
+    for (int groupKey: aggregate.getGroupSet().asList()) {
+      if (project.getProjects().get(groupKey) instanceof RexLiteral) {
+        if (project.getProjects().get(groupKey).isAlwaysTrue()) {
+          truePresent = true;
+        }
+        if (project.getProjects().get(groupKey).isAlwaysFalse()) {
+          falsePresent = true;
+        }
+      }
+    }
+
+    RelDataTypeFactory.Builder relDataTypeBuilder = 
rexBuilder.getTypeFactory().builder();
+    if (truePresent) {
+      b.add(rexBuilder.makeLiteral(true));
+      relDataTypeBuilder.add("T", SqlTypeName.BOOLEAN);
+    }
+    if (falsePresent) {
+      b.add(rexBuilder.makeLiteral(false));
+      relDataTypeBuilder.add("F", SqlTypeName.BOOLEAN);
+    }
+    tuples.add(b.build());
+    LogicalValues values = LogicalValues.create(builder.getCluster(), 
relDataTypeBuilder.build(),
+        tuples.build());
+
+    return values;
+  }
+
+  @Override public void onMatch(RelOptRuleCall call) {
+    final Aggregate aggregate = call.rel(0);
+    final Project project = call.rel(1);
+
+    RelBuilder builder = call.builder();
+    RexBuilder rexBuilder = builder.getRexBuilder();
+
+    builder.push(project.getInput());
+    LogicalValues values = getValues(aggregate, project, rexBuilder, builder);
+    builder.push(values);
+
+    builder.join(JoinRelType.INNER, rexBuilder.makeLiteral(true));
+    Join join = (Join) builder.build();
+
+    List<RexNode> newProjects = new ArrayList<>();
+    List<String> names = new ArrayList<>();
+
+    int falseIndex = join.getRowType().getFieldCount() - 1;
+    int trueIndex = join.getRight().getRowType().getFieldCount() == 1
+        ? falseIndex : falseIndex - 1;
+    for (Pair<RexNode, String> pair: project.getNamedProjects()) {
+      if (pair.getKey() instanceof RexLiteral) {
+        if (pair.getKey().isAlwaysTrue()) {
+          newProjects.add(RexInputRef.of(trueIndex, join.getRowType()));
+          names.add(join.getRowType().getFieldList().get(trueIndex).getName());
+        } else if (pair.getKey().isAlwaysFalse()) {
+          newProjects.add(RexInputRef.of(falseIndex, join.getRowType()));

Review comment:
       ```suggestion
             newProjects.add(builder.field("F"));
   ```

##########
File path: 
core/src/main/java/org/apache/calcite/rel/rules/GroupByConstantAddJoinRule.java
##########
@@ -0,0 +1,196 @@
+/*
+ * 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.calcite.rel.rules;
+
+import org.apache.calcite.plan.RelOptRuleCall;
+import org.apache.calcite.plan.RelRule;
+import org.apache.calcite.rel.core.Aggregate;
+import org.apache.calcite.rel.core.Join;
+import org.apache.calcite.rel.core.JoinRelType;
+import org.apache.calcite.rel.core.Project;
+import org.apache.calcite.rel.logical.LogicalAggregate;
+import org.apache.calcite.rel.logical.LogicalProject;
+import org.apache.calcite.rel.logical.LogicalValues;
+import org.apache.calcite.rel.type.RelDataTypeFactory;
+import org.apache.calcite.rex.RexBuilder;
+import org.apache.calcite.rex.RexInputRef;
+import org.apache.calcite.rex.RexLiteral;
+import org.apache.calcite.rex.RexNode;
+import org.apache.calcite.sql.type.SqlTypeFamily;
+import org.apache.calcite.sql.type.SqlTypeName;
+import org.apache.calcite.tools.RelBuilder;
+import org.apache.calcite.util.Pair;
+
+import com.google.common.collect.ImmutableList;
+
+import org.immutables.value.Value;
+
+import java.util.ArrayList;
+import java.util.List;
+
+/**
+ * Planner rule that recognizes  a {@link 
org.apache.calcite.rel.core.Aggregate}
+ * on top of a {@link org.apache.calcite.rel.core.Project} where the 
aggregate's group set
+ * contains boolean literals (true, false), and removes the literals from the 
group keys by joining
+ * with a dummy table of boolean literals.
+ *
+ * select avg(sal)
+ * from emp
+ * group by true;
+ *
+ * becomes
+ *
+ * select avg(sal)
+ * from emp, (select true x) dummy
+ * group by dummy.x;
+ */
[email protected]
+public class GroupByConstantAddJoinRule
+    extends RelRule<GroupByConstantAddJoinRule.Config> {
+
+  /**
+   * Creates a RelRule.
+   *
+   */
+  protected GroupByConstantAddJoinRule(Config config) {
+    super(config);
+  }
+
+  @Override public boolean matches(RelOptRuleCall call) {
+    final Aggregate aggregate = call.rel(0);
+    final Project project = call.rel(1);
+
+    // avoids transforming correlated queries
+    if (aggregate.getAggCallList().size() == 0) {
+      return false;
+    }
+
+    boolean hasBooleanLiteral = false;
+
+    for (int groupKey: aggregate.getGroupSet().asList()) {
+      if (groupKey >= aggregate.getRowType().getFieldCount()) {
+        continue;
+      }
+      hasBooleanLiteral = 
(aggregate.getRowType().getFieldList().get(groupKey).getType().getFamily()
+          == SqlTypeFamily.BOOLEAN)
+          && (project.getProjects().get(groupKey) instanceof RexLiteral);
+
+      if (hasBooleanLiteral) {
+        return true;
+      }
+    }
+
+    return hasBooleanLiteral;
+  }
+
+  private static LogicalValues getValues(
+      Aggregate aggregate, Project project, RexBuilder rexBuilder, RelBuilder 
builder) {
+    final ImmutableList.Builder<ImmutableList<RexLiteral>> tuples =
+        ImmutableList.builder();
+    ImmutableList.Builder<RexLiteral> b = ImmutableList.builder();
+    boolean truePresent = false;
+    boolean falsePresent = false;
+
+    for (int groupKey: aggregate.getGroupSet().asList()) {
+      if (project.getProjects().get(groupKey) instanceof RexLiteral) {
+        if (project.getProjects().get(groupKey).isAlwaysTrue()) {
+          truePresent = true;
+        }
+        if (project.getProjects().get(groupKey).isAlwaysFalse()) {
+          falsePresent = true;
+        }
+      }
+    }
+
+    RelDataTypeFactory.Builder relDataTypeBuilder = 
rexBuilder.getTypeFactory().builder();
+    if (truePresent) {
+      b.add(rexBuilder.makeLiteral(true));
+      relDataTypeBuilder.add("T", SqlTypeName.BOOLEAN);
+    }
+    if (falsePresent) {
+      b.add(rexBuilder.makeLiteral(false));
+      relDataTypeBuilder.add("F", SqlTypeName.BOOLEAN);
+    }
+    tuples.add(b.build());
+    LogicalValues values = LogicalValues.create(builder.getCluster(), 
relDataTypeBuilder.build(),
+        tuples.build());
+
+    return values;
+  }
+
+  @Override public void onMatch(RelOptRuleCall call) {
+    final Aggregate aggregate = call.rel(0);
+    final Project project = call.rel(1);
+
+    RelBuilder builder = call.builder();
+    RexBuilder rexBuilder = builder.getRexBuilder();
+
+    builder.push(project.getInput());
+    LogicalValues values = getValues(aggregate, project, rexBuilder, builder);
+    builder.push(values);
+
+    builder.join(JoinRelType.INNER, rexBuilder.makeLiteral(true));
+    Join join = (Join) builder.build();
+
+    List<RexNode> newProjects = new ArrayList<>();
+    List<String> names = new ArrayList<>();
+
+    int falseIndex = join.getRowType().getFieldCount() - 1;
+    int trueIndex = join.getRight().getRowType().getFieldCount() == 1
+        ? falseIndex : falseIndex - 1;
+    for (Pair<RexNode, String> pair: project.getNamedProjects()) {
+      if (pair.getKey() instanceof RexLiteral) {
+        if (pair.getKey().isAlwaysTrue()) {
+          newProjects.add(RexInputRef.of(trueIndex, join.getRowType()));
+          names.add(join.getRowType().getFieldList().get(trueIndex).getName());
+        } else if (pair.getKey().isAlwaysFalse()) {
+          newProjects.add(RexInputRef.of(falseIndex, join.getRowType()));
+          
names.add(join.getRowType().getFieldList().get(falseIndex).getName());
+        }
+      } else {
+        newProjects.add(pair.getKey());
+        names.add(pair.getValue());
+      }
+    }
+
+    Project newProject = LogicalProject.create(join, project.getHints(), 
newProjects, names);

Review comment:
       ```suggestion
      builder.project(newProjects);
   ```
   In general it is not a good idea to call directly the constructor or static 
factory of `RelNode`s inside a rule. Doing this you are forcing a certain type 
of expression (e.g., `Logical` in this case) which might not be compatible with 
the rest of the plan.
   
   Plus, the `builder` already has some logic that can simplify certain things 
like the project names in this case; I have the impression that appropriate 
names for the projects will be chosen even if you don't specify them explicitly.
   
   `project.copy` could be another option not sure which one is better in this 
case.

##########
File path: core/src/main/java/org/apache/calcite/sql/SqlDialect.java
##########
@@ -712,6 +712,10 @@ public boolean supportsCharSet() {
     return true;
   }
 
+  public boolean supportsGroupByBoolean() {

Review comment:
       I think the constant should be part of the method name; `GroupByBoolean` 
could refer to a boolean column. Please also add some javadoc around this 
method.

##########
File path: core/src/test/java/org/apache/calcite/test/RelOptRulesTest.java
##########
@@ -201,6 +202,30 @@ protected DiffRepository getDiffRepos() {
     return DiffRepository.lookup(RelOptRulesTest.class);
   }
 
+  @Test void testGroupByBooleanConstantSimple() {
+    HepProgramBuilder builder = new HepProgramBuilder();
+    builder.addRuleClass(GroupByConstantAddJoinRule.class);

Review comment:
       Same comment as before `addRuleClass` is not necessary.

##########
File path: 
core/src/main/java/org/apache/calcite/rel/rules/GroupByConstantAddJoinRule.java
##########
@@ -0,0 +1,196 @@
+/*
+ * 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.calcite.rel.rules;
+
+import org.apache.calcite.plan.RelOptRuleCall;
+import org.apache.calcite.plan.RelRule;
+import org.apache.calcite.rel.core.Aggregate;
+import org.apache.calcite.rel.core.Join;
+import org.apache.calcite.rel.core.JoinRelType;
+import org.apache.calcite.rel.core.Project;
+import org.apache.calcite.rel.logical.LogicalAggregate;
+import org.apache.calcite.rel.logical.LogicalProject;
+import org.apache.calcite.rel.logical.LogicalValues;
+import org.apache.calcite.rel.type.RelDataTypeFactory;
+import org.apache.calcite.rex.RexBuilder;
+import org.apache.calcite.rex.RexInputRef;
+import org.apache.calcite.rex.RexLiteral;
+import org.apache.calcite.rex.RexNode;
+import org.apache.calcite.sql.type.SqlTypeFamily;
+import org.apache.calcite.sql.type.SqlTypeName;
+import org.apache.calcite.tools.RelBuilder;
+import org.apache.calcite.util.Pair;
+
+import com.google.common.collect.ImmutableList;
+
+import org.immutables.value.Value;
+
+import java.util.ArrayList;
+import java.util.List;
+
+/**
+ * Planner rule that recognizes  a {@link 
org.apache.calcite.rel.core.Aggregate}
+ * on top of a {@link org.apache.calcite.rel.core.Project} where the 
aggregate's group set
+ * contains boolean literals (true, false), and removes the literals from the 
group keys by joining
+ * with a dummy table of boolean literals.
+ *
+ * select avg(sal)
+ * from emp
+ * group by true;
+ *
+ * becomes
+ *
+ * select avg(sal)
+ * from emp, (select true x) dummy
+ * group by dummy.x;
+ */
[email protected]
+public class GroupByConstantAddJoinRule
+    extends RelRule<GroupByConstantAddJoinRule.Config> {
+
+  /**
+   * Creates a RelRule.
+   *
+   */
+  protected GroupByConstantAddJoinRule(Config config) {
+    super(config);
+  }
+
+  @Override public boolean matches(RelOptRuleCall call) {
+    final Aggregate aggregate = call.rel(0);
+    final Project project = call.rel(1);
+
+    // avoids transforming correlated queries
+    if (aggregate.getAggCallList().size() == 0) {
+      return false;
+    }
+
+    boolean hasBooleanLiteral = false;
+
+    for (int groupKey: aggregate.getGroupSet().asList()) {
+      if (groupKey >= aggregate.getRowType().getFieldCount()) {
+        continue;
+      }
+      hasBooleanLiteral = 
(aggregate.getRowType().getFieldList().get(groupKey).getType().getFamily()
+          == SqlTypeFamily.BOOLEAN)
+          && (project.getProjects().get(groupKey) instanceof RexLiteral);
+
+      if (hasBooleanLiteral) {
+        return true;
+      }
+    }
+
+    return hasBooleanLiteral;
+  }
+
+  private static LogicalValues getValues(
+      Aggregate aggregate, Project project, RexBuilder rexBuilder, RelBuilder 
builder) {
+    final ImmutableList.Builder<ImmutableList<RexLiteral>> tuples =
+        ImmutableList.builder();
+    ImmutableList.Builder<RexLiteral> b = ImmutableList.builder();
+    boolean truePresent = false;
+    boolean falsePresent = false;
+
+    for (int groupKey: aggregate.getGroupSet().asList()) {
+      if (project.getProjects().get(groupKey) instanceof RexLiteral) {
+        if (project.getProjects().get(groupKey).isAlwaysTrue()) {
+          truePresent = true;
+        }
+        if (project.getProjects().get(groupKey).isAlwaysFalse()) {
+          falsePresent = true;
+        }
+      }
+    }
+
+    RelDataTypeFactory.Builder relDataTypeBuilder = 
rexBuilder.getTypeFactory().builder();
+    if (truePresent) {
+      b.add(rexBuilder.makeLiteral(true));
+      relDataTypeBuilder.add("T", SqlTypeName.BOOLEAN);
+    }
+    if (falsePresent) {
+      b.add(rexBuilder.makeLiteral(false));
+      relDataTypeBuilder.add("F", SqlTypeName.BOOLEAN);
+    }
+    tuples.add(b.build());
+    LogicalValues values = LogicalValues.create(builder.getCluster(), 
relDataTypeBuilder.build(),
+        tuples.build());
+
+    return values;
+  }
+
+  @Override public void onMatch(RelOptRuleCall call) {
+    final Aggregate aggregate = call.rel(0);
+    final Project project = call.rel(1);
+
+    RelBuilder builder = call.builder();
+    RexBuilder rexBuilder = builder.getRexBuilder();
+
+    builder.push(project.getInput());
+    LogicalValues values = getValues(aggregate, project, rexBuilder, builder);
+    builder.push(values);

Review comment:
       Do you really need the `getValues` method? How about the following: 
   
   `builder.values(new String[]{"T", "F"}, true, false)`
   
   If you are inside the `onMatch` method then you know for sure that you have 
boolean constants in the `Aggregate` otherwise `matches` would have returned 
`false` and you wouldn't enter here. Checking if you have both `true` and 
`false` doesn't worth the effort; you can include both by default. Doing this 
could simplify also the code below.

##########
File path: 
core/src/main/java/org/apache/calcite/sql/dialect/RedshiftSqlDialect.java
##########
@@ -106,4 +106,8 @@ public RedshiftSqlDialect(Context context) {
         new SqlUserDefinedTypeNameSpec(castSpec, SqlParserPos.ZERO),
         SqlParserPos.ZERO);
   }
+
+  @Override public boolean supportsGroupByBoolean() {
+    return false;
+  }

Review comment:
       Please do the respective change also in `InformixSqlDialect`.

##########
File path: 
core/src/test/java/org/apache/calcite/rel/rel2sql/RelToSqlConverterTest.java
##########
@@ -214,6 +214,13 @@ private static String toSql(RelNode root, SqlDialect 
dialect,
         .getSql();
   }
 
+  @Test void testGroupByBoolean() {

Review comment:
       ```suggestion
     @Test void testGroupByBooleanConstant() {
   ```

##########
File path: 
core/src/main/java/org/apache/calcite/rel/rules/GroupByConstantAddJoinRule.java
##########
@@ -0,0 +1,196 @@
+/*
+ * 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.calcite.rel.rules;
+
+import org.apache.calcite.plan.RelOptRuleCall;
+import org.apache.calcite.plan.RelRule;
+import org.apache.calcite.rel.core.Aggregate;
+import org.apache.calcite.rel.core.Join;
+import org.apache.calcite.rel.core.JoinRelType;
+import org.apache.calcite.rel.core.Project;
+import org.apache.calcite.rel.logical.LogicalAggregate;
+import org.apache.calcite.rel.logical.LogicalProject;
+import org.apache.calcite.rel.logical.LogicalValues;
+import org.apache.calcite.rel.type.RelDataTypeFactory;
+import org.apache.calcite.rex.RexBuilder;
+import org.apache.calcite.rex.RexInputRef;
+import org.apache.calcite.rex.RexLiteral;
+import org.apache.calcite.rex.RexNode;
+import org.apache.calcite.sql.type.SqlTypeFamily;
+import org.apache.calcite.sql.type.SqlTypeName;
+import org.apache.calcite.tools.RelBuilder;
+import org.apache.calcite.util.Pair;
+
+import com.google.common.collect.ImmutableList;
+
+import org.immutables.value.Value;
+
+import java.util.ArrayList;
+import java.util.List;
+
+/**
+ * Planner rule that recognizes  a {@link 
org.apache.calcite.rel.core.Aggregate}
+ * on top of a {@link org.apache.calcite.rel.core.Project} where the 
aggregate's group set
+ * contains boolean literals (true, false), and removes the literals from the 
group keys by joining
+ * with a dummy table of boolean literals.
+ *
+ * select avg(sal)
+ * from emp
+ * group by true;
+ *
+ * becomes
+ *
+ * select avg(sal)
+ * from emp, (select true x) dummy
+ * group by dummy.x;
+ */
[email protected]
+public class GroupByConstantAddJoinRule
+    extends RelRule<GroupByConstantAddJoinRule.Config> {
+
+  /**
+   * Creates a RelRule.
+   *
+   */
+  protected GroupByConstantAddJoinRule(Config config) {
+    super(config);
+  }
+
+  @Override public boolean matches(RelOptRuleCall call) {
+    final Aggregate aggregate = call.rel(0);
+    final Project project = call.rel(1);
+
+    // avoids transforming correlated queries
+    if (aggregate.getAggCallList().size() == 0) {
+      return false;
+    }
+
+    boolean hasBooleanLiteral = false;
+
+    for (int groupKey: aggregate.getGroupSet().asList()) {
+      if (groupKey >= aggregate.getRowType().getFieldCount()) {
+        continue;
+      }
+      hasBooleanLiteral = 
(aggregate.getRowType().getFieldList().get(groupKey).getType().getFamily()
+          == SqlTypeFamily.BOOLEAN)
+          && (project.getProjects().get(groupKey) instanceof RexLiteral);
+
+      if (hasBooleanLiteral) {
+        return true;
+      }
+    }
+
+    return hasBooleanLiteral;
+  }
+
+  private static LogicalValues getValues(
+      Aggregate aggregate, Project project, RexBuilder rexBuilder, RelBuilder 
builder) {
+    final ImmutableList.Builder<ImmutableList<RexLiteral>> tuples =
+        ImmutableList.builder();
+    ImmutableList.Builder<RexLiteral> b = ImmutableList.builder();
+    boolean truePresent = false;
+    boolean falsePresent = false;
+
+    for (int groupKey: aggregate.getGroupSet().asList()) {
+      if (project.getProjects().get(groupKey) instanceof RexLiteral) {
+        if (project.getProjects().get(groupKey).isAlwaysTrue()) {
+          truePresent = true;
+        }
+        if (project.getProjects().get(groupKey).isAlwaysFalse()) {
+          falsePresent = true;
+        }
+      }
+    }
+
+    RelDataTypeFactory.Builder relDataTypeBuilder = 
rexBuilder.getTypeFactory().builder();
+    if (truePresent) {
+      b.add(rexBuilder.makeLiteral(true));
+      relDataTypeBuilder.add("T", SqlTypeName.BOOLEAN);
+    }
+    if (falsePresent) {
+      b.add(rexBuilder.makeLiteral(false));
+      relDataTypeBuilder.add("F", SqlTypeName.BOOLEAN);
+    }
+    tuples.add(b.build());
+    LogicalValues values = LogicalValues.create(builder.getCluster(), 
relDataTypeBuilder.build(),
+        tuples.build());
+
+    return values;
+  }
+
+  @Override public void onMatch(RelOptRuleCall call) {
+    final Aggregate aggregate = call.rel(0);
+    final Project project = call.rel(1);
+
+    RelBuilder builder = call.builder();
+    RexBuilder rexBuilder = builder.getRexBuilder();
+
+    builder.push(project.getInput());
+    LogicalValues values = getValues(aggregate, project, rexBuilder, builder);
+    builder.push(values);
+
+    builder.join(JoinRelType.INNER, rexBuilder.makeLiteral(true));
+    Join join = (Join) builder.build();
+
+    List<RexNode> newProjects = new ArrayList<>();
+    List<String> names = new ArrayList<>();
+
+    int falseIndex = join.getRowType().getFieldCount() - 1;
+    int trueIndex = join.getRight().getRowType().getFieldCount() == 1
+        ? falseIndex : falseIndex - 1;
+    for (Pair<RexNode, String> pair: project.getNamedProjects()) {
+      if (pair.getKey() instanceof RexLiteral) {
+        if (pair.getKey().isAlwaysTrue()) {
+          newProjects.add(RexInputRef.of(trueIndex, join.getRowType()));
+          names.add(join.getRowType().getFieldList().get(trueIndex).getName());
+        } else if (pair.getKey().isAlwaysFalse()) {
+          newProjects.add(RexInputRef.of(falseIndex, join.getRowType()));
+          
names.add(join.getRowType().getFieldList().get(falseIndex).getName());
+        }
+      } else {
+        newProjects.add(pair.getKey());
+        names.add(pair.getValue());
+      }
+    }
+
+    Project newProject = LogicalProject.create(join, project.getHints(), 
newProjects, names);
+    Aggregate newAggregate = aggregate.copy(aggregate.getTraitSet(), 
newProject,
+        aggregate.getGroupSet(), aggregate.getGroupSets(), 
aggregate.getAggCallList());
+    call.transformTo(newAggregate);
+  }
+
+  /**
+   * Rule Configuration.
+   */
+  @Value.Immutable
+  public interface Config extends RelRule.Config {
+    Config DEFAULT = ImmutableGroupByConstantAddJoinRule.Config.of()
+        .withOperandFor(LogicalAggregate.class, LogicalProject.class);

Review comment:
       Maybe the default rule should match the abstract classes and not the 
logical ones (i.e., Aggregate.class, `Project.class`).

##########
File path: core/src/test/java/org/apache/calcite/test/RelOptRulesTest.java
##########
@@ -201,6 +202,30 @@ protected DiffRepository getDiffRepos() {
     return DiffRepository.lookup(RelOptRulesTest.class);
   }
 
+  @Test void testGroupByBooleanConstantSimple() {
+    HepProgramBuilder builder = new HepProgramBuilder();
+    builder.addRuleClass(GroupByConstantAddJoinRule.class);
+    HepPlanner hepPlanner = new HepPlanner(builder.build());
+    hepPlanner.addRule(GroupByConstantAddJoinRule.Config.DEFAULT.toRule());
+
+    final String query = "select avg(sal)\n"
+        + "from emp\n"
+        + "group by true";
+    sql(query).with(hepPlanner).check();
+  }
+
+  @Test void testGroupByBooleanConstantMultiple() {
+    HepProgramBuilder builder = new HepProgramBuilder();
+    builder.addRuleClass(GroupByConstantAddJoinRule.class);

Review comment:
       Idem

##########
File path: core/src/main/java/org/apache/calcite/rel/rel2sql/SqlImplementor.java
##########
@@ -161,8 +164,20 @@ protected SqlImplementor(SqlDialect dialect) {
 
   /** Visits a relational expression that has no parent. */
   public final Result visitRoot(RelNode r) {
+    RelNode best;
+    if (!this.dialect.supportsGroupByBoolean()) {
+      HepProgramBuilder hepProgramBuilder = new HepProgramBuilder();
+      hepProgramBuilder.addRuleClass(GroupByConstantAddJoinRule.class);

Review comment:
       I don't think you need to call `addRuleClass`. You can use 
`addRuleInstance` and remove the call to `addRule` from below.

##########
File path: 
core/src/test/java/org/apache/calcite/rel/rel2sql/RelToSqlConverterTest.java
##########
@@ -214,6 +214,13 @@ private static String toSql(RelNode root, SqlDialect 
dialect,
         .getSql();
   }
 
+  @Test void testGroupByBoolean() {
+    String query = "select avg(\"salary\") from \"employee\" group by true";
+    String expected = "SELECT AVG(\"employee\".\"salary\")\nFROM 
\"foodmart\".\"employee\",\n"
+        + "(VALUES (TRUE)) AS \"t\" (\"T\")\nGROUP BY \"t\".\"T\"";
+    sql(query).withRedshift().ok(expected);

Review comment:
       Include also Informix.

##########
File path: 
core/src/main/java/org/apache/calcite/rel/rules/GroupByConstantAddJoinRule.java
##########
@@ -0,0 +1,196 @@
+/*
+ * 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.calcite.rel.rules;
+
+import org.apache.calcite.plan.RelOptRuleCall;
+import org.apache.calcite.plan.RelRule;
+import org.apache.calcite.rel.core.Aggregate;
+import org.apache.calcite.rel.core.Join;
+import org.apache.calcite.rel.core.JoinRelType;
+import org.apache.calcite.rel.core.Project;
+import org.apache.calcite.rel.logical.LogicalAggregate;
+import org.apache.calcite.rel.logical.LogicalProject;
+import org.apache.calcite.rel.logical.LogicalValues;
+import org.apache.calcite.rel.type.RelDataTypeFactory;
+import org.apache.calcite.rex.RexBuilder;
+import org.apache.calcite.rex.RexInputRef;
+import org.apache.calcite.rex.RexLiteral;
+import org.apache.calcite.rex.RexNode;
+import org.apache.calcite.sql.type.SqlTypeFamily;
+import org.apache.calcite.sql.type.SqlTypeName;
+import org.apache.calcite.tools.RelBuilder;
+import org.apache.calcite.util.Pair;
+
+import com.google.common.collect.ImmutableList;
+
+import org.immutables.value.Value;
+
+import java.util.ArrayList;
+import java.util.List;
+
+/**
+ * Planner rule that recognizes  a {@link 
org.apache.calcite.rel.core.Aggregate}
+ * on top of a {@link org.apache.calcite.rel.core.Project} where the 
aggregate's group set
+ * contains boolean literals (true, false), and removes the literals from the 
group keys by joining
+ * with a dummy table of boolean literals.
+ *
+ * select avg(sal)
+ * from emp
+ * group by true;
+ *
+ * becomes
+ *
+ * select avg(sal)
+ * from emp, (select true x) dummy
+ * group by dummy.x;
+ */
[email protected]
+public class GroupByConstantAddJoinRule

Review comment:
       If the class is not meant to be extended consider declaring it `final`.

##########
File path: 
core/src/main/java/org/apache/calcite/rel/rules/GroupByConstantAddJoinRule.java
##########
@@ -0,0 +1,196 @@
+/*
+ * 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.calcite.rel.rules;
+
+import org.apache.calcite.plan.RelOptRuleCall;
+import org.apache.calcite.plan.RelRule;
+import org.apache.calcite.rel.core.Aggregate;
+import org.apache.calcite.rel.core.Join;
+import org.apache.calcite.rel.core.JoinRelType;
+import org.apache.calcite.rel.core.Project;
+import org.apache.calcite.rel.logical.LogicalAggregate;
+import org.apache.calcite.rel.logical.LogicalProject;
+import org.apache.calcite.rel.logical.LogicalValues;
+import org.apache.calcite.rel.type.RelDataTypeFactory;
+import org.apache.calcite.rex.RexBuilder;
+import org.apache.calcite.rex.RexInputRef;
+import org.apache.calcite.rex.RexLiteral;
+import org.apache.calcite.rex.RexNode;
+import org.apache.calcite.sql.type.SqlTypeFamily;
+import org.apache.calcite.sql.type.SqlTypeName;
+import org.apache.calcite.tools.RelBuilder;
+import org.apache.calcite.util.Pair;
+
+import com.google.common.collect.ImmutableList;
+
+import org.immutables.value.Value;
+
+import java.util.ArrayList;
+import java.util.List;
+
+/**
+ * Planner rule that recognizes  a {@link 
org.apache.calcite.rel.core.Aggregate}
+ * on top of a {@link org.apache.calcite.rel.core.Project} where the 
aggregate's group set
+ * contains boolean literals (true, false), and removes the literals from the 
group keys by joining
+ * with a dummy table of boolean literals.
+ *
+ * select avg(sal)
+ * from emp
+ * group by true;
+ *
+ * becomes
+ *
+ * select avg(sal)
+ * from emp, (select true x) dummy
+ * group by dummy.x;
+ */
[email protected]
+public class GroupByConstantAddJoinRule
+    extends RelRule<GroupByConstantAddJoinRule.Config> {
+
+  /**
+   * Creates a RelRule.
+   *
+   */
+  protected GroupByConstantAddJoinRule(Config config) {
+    super(config);
+  }
+
+  @Override public boolean matches(RelOptRuleCall call) {
+    final Aggregate aggregate = call.rel(0);
+    final Project project = call.rel(1);
+
+    // avoids transforming correlated queries
+    if (aggregate.getAggCallList().size() == 0) {
+      return false;
+    }
+
+    boolean hasBooleanLiteral = false;

Review comment:
       If you `return` from inside the `for-loop` I guess you can move the 
declaration inside the loop.

##########
File path: 
core/src/main/java/org/apache/calcite/rel/rules/GroupByConstantAddJoinRule.java
##########
@@ -0,0 +1,196 @@
+/*
+ * 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.calcite.rel.rules;
+
+import org.apache.calcite.plan.RelOptRuleCall;
+import org.apache.calcite.plan.RelRule;
+import org.apache.calcite.rel.core.Aggregate;
+import org.apache.calcite.rel.core.Join;
+import org.apache.calcite.rel.core.JoinRelType;
+import org.apache.calcite.rel.core.Project;
+import org.apache.calcite.rel.logical.LogicalAggregate;
+import org.apache.calcite.rel.logical.LogicalProject;
+import org.apache.calcite.rel.logical.LogicalValues;
+import org.apache.calcite.rel.type.RelDataTypeFactory;
+import org.apache.calcite.rex.RexBuilder;
+import org.apache.calcite.rex.RexInputRef;
+import org.apache.calcite.rex.RexLiteral;
+import org.apache.calcite.rex.RexNode;
+import org.apache.calcite.sql.type.SqlTypeFamily;
+import org.apache.calcite.sql.type.SqlTypeName;
+import org.apache.calcite.tools.RelBuilder;
+import org.apache.calcite.util.Pair;
+
+import com.google.common.collect.ImmutableList;
+
+import org.immutables.value.Value;
+
+import java.util.ArrayList;
+import java.util.List;
+
+/**
+ * Planner rule that recognizes  a {@link 
org.apache.calcite.rel.core.Aggregate}
+ * on top of a {@link org.apache.calcite.rel.core.Project} where the 
aggregate's group set
+ * contains boolean literals (true, false), and removes the literals from the 
group keys by joining
+ * with a dummy table of boolean literals.
+ *
+ * select avg(sal)
+ * from emp
+ * group by true;
+ *
+ * becomes
+ *
+ * select avg(sal)
+ * from emp, (select true x) dummy
+ * group by dummy.x;
+ */
[email protected]
+public class GroupByConstantAddJoinRule
+    extends RelRule<GroupByConstantAddJoinRule.Config> {
+
+  /**
+   * Creates a RelRule.
+   *
+   */
+  protected GroupByConstantAddJoinRule(Config config) {
+    super(config);
+  }
+
+  @Override public boolean matches(RelOptRuleCall call) {
+    final Aggregate aggregate = call.rel(0);
+    final Project project = call.rel(1);
+
+    // avoids transforming correlated queries
+    if (aggregate.getAggCallList().size() == 0) {
+      return false;
+    }
+
+    boolean hasBooleanLiteral = false;
+
+    for (int groupKey: aggregate.getGroupSet().asList()) {
+      if (groupKey >= aggregate.getRowType().getFieldCount()) {
+        continue;
+      }
+      hasBooleanLiteral = 
(aggregate.getRowType().getFieldList().get(groupKey).getType().getFamily()
+          == SqlTypeFamily.BOOLEAN)
+          && (project.getProjects().get(groupKey) instanceof RexLiteral);
+
+      if (hasBooleanLiteral) {
+        return true;
+      }
+    }
+
+    return hasBooleanLiteral;

Review comment:
       ```suggestion
       return false;
   ```

##########
File path: 
core/src/test/java/org/apache/calcite/rel/rel2sql/RelToSqlConverterTest.java
##########
@@ -214,6 +214,13 @@ private static String toSql(RelNode root, SqlDialect 
dialect,
         .getSql();
   }
 
+  @Test void testGroupByBoolean() {
+    String query = "select avg(\"salary\") from \"employee\" group by true";
+    String expected = "SELECT AVG(\"employee\".\"salary\")\nFROM 
\"foodmart\".\"employee\",\n"
+        + "(VALUES (TRUE)) AS \"t\" (\"T\")\nGROUP BY \"t\".\"T\"";

Review comment:
       Did you verify that the generated query works in `Redshift? This is 
slightly different from the query discussed in the JIRA so let's ensure it 
works:
   ```
   select avg(sal)
    from emp, (select true x) dummy
    group by dummy.x;
   ```
   




-- 
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]


Reply via email to