|
17 | 17 | package org.apache.spark.sql.catalyst.parser |
18 | 18 |
|
19 | 19 | import org.apache.spark.sql.catalyst.expressions.{Expression, Literal} |
| 20 | +import org.apache.spark.sql.catalyst.types.DataTypeUtils |
| 21 | +import org.apache.spark.sql.util.SchemaUtils |
20 | 22 |
|
21 | 23 | /** |
22 | 24 | * Handler for parameter substitution across different Spark SQL contexts. |
@@ -107,14 +109,37 @@ object ParameterHandler { |
107 | 109 | * @param expr The expression to convert (must be a Literal) |
108 | 110 | * @return SQL string representation |
109 | 111 | */ |
110 | | - private def convertToSql(expr: Expression): String = expr match { |
111 | | - case lit: Literal => lit.sql |
112 | | - case other => |
113 | | - throw new IllegalArgumentException( |
114 | | - s"ParameterHandler only accepts resolved Literal expressions. " + |
115 | | - s"Received: ${other.getClass.getSimpleName}. " + |
116 | | - s"All parameters must be resolved using SparkSession.resolveAndValidateParameters " + |
117 | | - s"before being passed to the pre-parser.") |
| 112 | + private def convertToSql(expr: Expression): String = { |
| 113 | + // Converts an expression to its SQL representation. If the expression's type contains collated |
| 114 | + // types, strips collations from nested literals and wraps the whole expression in |
| 115 | + // CAST to preserve the collation with implicit strength. Without this, Literal.sql |
| 116 | + // produces `'value' COLLATE collationName` which re-parses with explicit strength. |
| 117 | + def toSqlWithImplicitCollation(e: Expression): String = { |
| 118 | + if (!DataTypeUtils.hasNonDefaultStringCharOrVarcharType(e.dataType)) { |
| 119 | + e.sql |
| 120 | + } else { |
| 121 | + val stripped = e.transform { |
| 122 | + case lit: Literal |
| 123 | + if DataTypeUtils.hasNonDefaultStringCharOrVarcharType(lit.dataType) => |
| 124 | + Literal.create( |
| 125 | + lit.value, SchemaUtils.replaceCollatedStringWithString(lit.dataType)) |
| 126 | + } |
| 127 | + s"CAST(${stripped.sql} AS ${e.dataType.sql})" |
| 128 | + } |
| 129 | + } |
| 130 | + |
| 131 | + expr match { |
| 132 | + case lit: Literal if lit.value == null => |
| 133 | + lit.sql |
| 134 | + case lit: Literal => |
| 135 | + toSqlWithImplicitCollation(lit) |
| 136 | + case other => |
| 137 | + throw new IllegalArgumentException( |
| 138 | + s"ParameterHandler only accepts resolved Literal expressions. " + |
| 139 | + s"Received: ${other.getClass.getSimpleName}. " + |
| 140 | + s"All parameters must be resolved using SparkSession.resolveAndValidateParameters " + |
| 141 | + s"before being passed to the pre-parser.") |
| 142 | + } |
118 | 143 | } |
119 | 144 |
|
120 | 145 | /** |
|
0 commit comments