cloud-fan commented on code in PR #56499:
URL: https://github.com/apache/spark/pull/56499#discussion_r3454224176


##########
sql/catalyst/src/test/scala/org/apache/spark/sql/catalyst/plans/ConstraintPropagationSuite.scala:
##########
@@ -427,4 +428,115 @@ class ConstraintPropagationSuite extends PlanTest {
       assert(aliasedRelation.analyze.constraints.isEmpty)
     }
   }
+
+  test("infer range constraints by substituting attr=literal bindings") {
+    // When a.pt = '20260610' (modeled as a.k = 5) and b.v >= a.k are both 
present,
+    // inferAdditionalConstraints should emit b.v >= 5.
+    val tr = LocalRelation($"k".int, $"v".int)
+    val a = resolveColumn(tr, "k")
+    val b = resolveColumn(tr, "v")
+
+    val constraints = ExpressionSet(Seq(
+      EqualTo(a, Literal(5)),
+      GreaterThanOrEqual(b, a),
+      LessThanOrEqual(b, Add(a, Literal(10)))))
+
+    val helper = new ConstraintHelper {}
+    val inferred = helper.inferConstraintsFromLiteralBindings(constraints)
+
+    // b >= 5 must be inferred
+    assert(inferred.exists {
+      case GreaterThanOrEqual(attr: Attribute, Literal(v, IntegerType)) =>
+        attr.semanticEquals(b) && v == 5
+      case _ => false
+    }, s"Expected b >= 5 in inferred constraints; got: $inferred")
+
+    // b <= 15 must be inferred (a + 10 with a=5, i.e. Add(Literal(5), 
Literal(10)))
+    assert(inferred.exists {
+      case LessThanOrEqual(attr: Attribute, rhs) if attr.semanticEquals(b) =>
+        // rhs is either Literal(15) or Add(Literal(5), Literal(10)) - both 
are foldable
+        rhs.foldable && rhs.eval(null) == 15
+      case _ => false
+    }, s"Expected b <= 15 in inferred constraints; got: $inferred")
+  }
+
+  test("infer range constraints: literal on left side of EqualTo") {
+    val tr = LocalRelation($"k".int, $"v".int)
+    val a = resolveColumn(tr, "k")
+    val b = resolveColumn(tr, "v")
+
+    // Literal appears on the left: 5 = a.k
+    val constraints = ExpressionSet(Seq(
+      EqualTo(Literal(5), a),
+      GreaterThanOrEqual(b, a)))
+
+    val helper = new ConstraintHelper {}
+    val inferred = helper.inferConstraintsFromLiteralBindings(constraints)
+
+    assert(inferred.exists {
+      case GreaterThanOrEqual(attr: Attribute, Literal(v, IntegerType)) =>
+        attr.semanticEquals(b) && v == 5
+      case _ => false
+    }, s"Expected b >= 5 in inferred constraints; got: $inferred")
+  }
+
+  test("infer constraints by substituting attr=literal bindings into EqualTo 
expressions") {
+    val tr = LocalRelation($"k".int, $"v".int)
+    val a = resolveColumn(tr, "k")
+    val b = resolveColumn(tr, "v")
+
+    val constraints = ExpressionSet(Seq(
+      EqualTo(a, Literal(5)),
+      EqualTo(b, Add(a, Literal(1)))))
+
+    val helper = new ConstraintHelper {}
+    val inferred = helper.inferConstraintsFromLiteralBindings(constraints)
+
+    // b = a + 1 with a = 5 should infer b = 5 + 1 (foldable to 6)
+    assert(inferred.exists {
+      case EqualTo(attr: Attribute, rhs) if attr.semanticEquals(b) =>
+        rhs.foldable && rhs.eval(null) == 6
+      case _ => false
+    }, s"Expected b = 6 in inferred constraints; got: $inferred")
+  }
+
+  test("non-deterministic constraints inferred from literal substitution are 
filtered out") {
+    val tr = LocalRelation($"k".int, $"v".int)
+    val a = resolveColumn(tr, "k")
+    val b = resolveColumn(tr, "v")
+
+    // b >= a.k + Rand(); after substituting a.k=5 the result is 
non-deterministic.
+    // inferAdditionalConstraints may emit it, but the plan-level constraints 
filter must drop it.
+    val randExpr = Add(a, Cast(Rand(0L), IntegerType))
+    val condition = EqualTo(a, Literal(5)) && GreaterThanOrEqual(b, randExpr)
+    val plan = Filter(condition, tr.analyze)
+
+    assert(!plan.analyze.constraints.exists {
+      case GreaterThanOrEqual(attr: Attribute, _) => attr.semanticEquals(b)
+      case _ => false
+    }, s"Should not keep non-deterministic constraint in plan constraints; " +
+      s"got: ${plan.analyze.constraints}")
+  }
+
+  test("do not infer constraints by substituting non-binary-stable collated 
attributes") {
+    // a is UTF8_LCASE (case-insensitive). a = 'hello' only tells us a is 
case-insensitively
+    // equal to 'hello'; it could still be 'HELLO'. Substituting a with 
'hello' into a
+    // UTF8_BINARY comparison like b >= a would infer b >= 'hello', which is 
wrong.
+    val a = AttributeReference("a", 
StringType(CollationFactory.UTF8_LCASE_COLLATION_ID))()
+    val b = AttributeReference("b", StringType)()
+    val hello = Literal("hello")
+
+    val constraints = ExpressionSet(Seq(
+      EqualTo(a, hello),
+      GreaterThanOrEqual(b, a)))
+
+    val helper = new ConstraintHelper {}
+    val inferred = helper.inferAdditionalConstraints(constraints)

Review Comment:
   This collation regression test is now vacuous. Literal substitution moved 
out of `inferAdditionalConstraints` into `inferConstraintsFromLiteralBindings`, 
so `inferAdditionalConstraints` no longer has any `EqualTo(attr, Literal)` case 
— for `{EqualTo(a, 'hello'), GreaterThanOrEqual(b, a)}` it matches nothing and 
returns empty. The assertion `!inferred.exists { b >= 'hello' }` therefore 
passes trivially, and would still pass even if the `isBinaryStable` guard were 
deleted from `inferConstraintsFromLiteralBindings` — so it no longer protects 
the collation fix it's named for.
   
   The three sibling tests (the range / literal-on-left / EqualTo-expression 
cases) correctly call `inferConstraintsFromLiteralBindings`; this one looks 
like it was missed in the rename. The production guard itself is correct, so 
this is a test-only fix:
   
   ```suggestion
       val inferred = helper.inferConstraintsFromLiteralBindings(constraints)
   ```



##########
sql/catalyst/src/test/scala/org/apache/spark/sql/catalyst/plans/ConstraintPropagationSuite.scala:
##########
@@ -427,4 +428,115 @@ class ConstraintPropagationSuite extends PlanTest {
       assert(aliasedRelation.analyze.constraints.isEmpty)
     }
   }
+
+  test("infer range constraints by substituting attr=literal bindings") {
+    // When a.pt = '20260610' (modeled as a.k = 5) and b.v >= a.k are both 
present,
+    // inferAdditionalConstraints should emit b.v >= 5.

Review Comment:
   Stale method name in the comment — this test (and the one at line 509) 
describe `inferAdditionalConstraints`, but the substitution they exercise is 
now done by `inferConstraintsFromLiteralBindings` (this test body even calls 
it). Worth updating both comments to name the new method.



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