diff --git a/core/src/main/java/org/apache/calcite/rex/LogicVisitor.java b/core/src/main/java/org/apache/calcite/rex/LogicVisitor.java
index 68a0d0aa963..b18613b104a 100644
--- a/core/src/main/java/org/apache/calcite/rex/LogicVisitor.java
+++ b/core/src/main/java/org/apache/calcite/rex/LogicVisitor.java
@@ -31,13 +31,20 @@
import static java.util.Objects.requireNonNull;
/**
- * Visitor pattern for traversing a tree of {@link RexNode} objects.
+ * Visitor that, given the {@link Logic} in force at the root of an
+ * expression, computes the Logic in force at every occurrence of a sought
+ * sub-expression {@code seek}. Results are collected in {@code logicCollection}.
+ *
+ *
This value is meaningful only for expressions that evaluate to Boolean values.
*/
public class LogicVisitor extends RexUnaryBiVisitor<@Nullable Logic> {
private final RexNode seek;
private final Collection logicCollection;
- /** Creates a LogicVisitor. */
+ /** Creates a LogicVisitor.
+ *
+ * @param seek Expression whose occurrences to find
+ * @param logicCollection Receives the Logic in force for each occurrence of {@code seek} */
private LogicVisitor(RexNode seek, Collection logicCollection) {
super(true);
this.seek = seek;
@@ -51,6 +58,14 @@ private LogicVisitor(RexNode seek, Collection logicCollection) {
* answer) with the fewest possibilities (that is, we prefer one that
* returns [true as true, false as false, unknown as false] over one that
* distinguishes false from unknown).
+ *
+ * If {@code seek} occurs multiple times, the result is
+ * a single Logic that is safe for every one of them. If the occurrences
+ * are evaluated under different Logic values, the result is
+ * {@link Logic#TRUE_FALSE_UNKNOWN}, which is safe for any occurrence.
+ *
+ * @throws IllegalArgumentException if {@code seek} does not occur in
+ * {@code nodes}
*/
public static Logic find(Logic logic, List nodes,
RexNode seek) {
@@ -74,6 +89,9 @@ public static Logic find(Logic logic, List nodes,
}
}
+ /** Appends to {@code logicList}, for each occurrence of {@code seek}
+ * within {@code node} in depth-first order, the Logic in force at that
+ * occurrence. */
public static void collect(RexNode node, RexNode seek, Logic logic,
List logicList) {
node.accept(new LogicVisitor(seek, logicList), logic);
@@ -137,6 +155,12 @@ public static void collect(RexNode node, RexNode seek, Logic logic,
@Override public @Nullable Logic visitFieldAccess(RexFieldAccess fieldAccess,
@Nullable Logic arg) {
+ // Not a Boolean value
+ Logic logic = requireNonNull(arg, "arg");
+ if (logic == Logic.TRUE) {
+ logic = Logic.TRUE_FALSE_UNKNOWN;
+ }
+ super.visitFieldAccess(fieldAccess, logic);
return end(fieldAccess, arg);
}
diff --git a/core/src/test/resources/sql/sub-query.iq b/core/src/test/resources/sql/sub-query.iq
index 5d1bbb73867..08c5bcf6e4b 100644
--- a/core/src/test/resources/sql/sub-query.iq
+++ b/core/src/test/resources/sql/sub-query.iq
@@ -10152,3 +10152,16 @@ ORDER BY emp.ename;
!ok
# End sub-query.iq
+
+# [CALCITE-7719] Field access on an element of a ROW array built by a
+# sub-query raises IllegalArgumentException.
+select t.a[1]."EXPR$0"."EXPR$1" as v
+from (select array(select ROW(ROW(1, 2), 3) from (values (0))) as a) as t;
++---+
+| V |
++---+
+| 2 |
++---+
+(1 row)
+
+!ok