sunchao commented on code in PR #58779:
URL: https://github.com/apache/spark/pull/58779#discussion_r4018751683


##########
sql/catalyst/src/main/scala/org/apache/spark/sql/catalyst/expressions/nullExpressions.scala:
##########
@@ -187,8 +187,18 @@ private case class TypedNullLiteral(child: Expression)
 
   override lazy val replacement: Expression = Literal.create(null, 
child.dataType)
 
-  override protected def withNewChildInternal(newChild: Expression): 
TypedNullLiteral =
-    copy(child = newChild)
+  override protected def withNewChildInternal(newChild: Expression): 
Expression =
+    TypedNullLiteral.create(newChild)
+}
+
+private object TypedNullLiteral {
+  def create(child: Expression): Expression = {
+    if (child.resolved) {
+      Literal.create(null, child.dataType)

Review Comment:
   Confirmed fixed at e0e60fd2b38d1c58a3e12eec478c93c7a11d67f6: the typed 
marker is synchronized with the coerced return branch, and With refreshes its 
references before body coercion. The new regression covering both ANSI and 
inlining settings passed in the exact-commit CI reports. Resolving this thread; 
I reported a separate reproduced single-pass view-collation issue in the new 
review.



##########
sql/catalyst/src/main/scala/org/apache/spark/sql/catalyst/expressions/nullExpressions.scala:
##########
@@ -187,8 +187,17 @@ private case class TypedNullLiteral(child: Expression)
 
   override lazy val replacement: Expression = Literal.create(null, 
child.dataType)
 
-  override protected def withNewChildInternal(newChild: Expression): 
TypedNullLiteral =
-    copy(child = newChild)
+  override protected def withNewChildInternal(newChild: Expression): 
Expression =
+    TypedNullLiteral.create(newChild)
+}
+
+private[sql] object TypedNullLiteral {
+  /**
+   * Replaces a resolved child with a typed null marker to avoid 3^n growth in 
nested `NullIf`
+   * plans.
+   */
+  def create(child: Expression): TypedNullLiteral = TypedNullLiteral(
+    if (child.resolved) Literal.create(null, child.dataType) else child)

Review Comment:
   [P2] Preserve the generated null's collation during view resolution
   
   This generated `Literal` is revisited by the single-pass resolver and 
receives the view's default collation, even though its type came from the 
operand. With the default `spark.sql.alwaysInlineCommonExpr=false`, this 
reproduces on the current compiled build:
   
   ```sql
   SET spark.sql.analyzer.singlePassResolver.enabled=false;
   CREATE TABLE t (c STRING) USING parquet;
   INSERT INTO t VALUES ('a');
   CREATE VIEW v DEFAULT COLLATION UTF8_LCASE AS
     SELECT lower(nullif(c, c)) AS n FROM t;
   SET spark.sql.analyzer.singlePassResolver.enabled=true;
   SELECT * FROM v;
   ```
   
   The last query fails with `INDETERMINATE_COLLATION_IN_EXPRESSION` for 
`lower(nullif(c, c))`; the base-code control returns null. 
`DefaultCollationTypeCoercion` recolors the generated null to UTF8_LCASE while 
the false branch's `CommonExpressionRef` remains UTF8_BINARY. Collation 
coercion then wraps the marker in a cast, so the new `IfTypeCoercion` case 
cannot recognize and repair it. Please keep this generated null's 
operand-derived collation intact, or synchronize it before collation coercion 
wraps it, and add a single-pass view regression.



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