cloud-fan commented on code in PR #52173: URL: https://github.com/apache/spark/pull/52173#discussion_r2338887413
########## sql/catalyst/src/main/scala/org/apache/spark/sql/catalyst/analysis/parameters.scala: ########## @@ -203,6 +226,64 @@ object BindParameters extends Rule[LogicalPlan] with QueryErrorsBase { args(posToIndex(pos)) } + case GeneralParameterizedQuery(child, args, paramNames) + if !child.containsPattern(UNRESOLVED_WITH) && + args.forall(_.resolved) => + + // Collect parameter types used in the query and validate no mixing + val namedParams = scala.collection.mutable.Set.empty[String] + val positionalParams = scala.collection.mutable.Set.empty[Int] + bind(child) { + case NamedParameter(name) => namedParams.add(name); NamedParameter(name) + case p @ PosParameter(pos) => positionalParams.add(pos); p + } + + // Validate: no mixing of positional and named parameters + if (namedParams.nonEmpty && positionalParams.nonEmpty) { + throw QueryCompilationErrors.invalidQueryMixedQueryParameters() + } + + // Validate: if query uses named parameters, all USING expressions must have names + if (namedParams.nonEmpty && positionalParams.isEmpty) { + val unnamedExpressions = paramNames.zipWithIndex.collect { + case (null, index) => index + case ("", index) => index // empty strings are unnamed + } + if (unnamedExpressions.nonEmpty) { + val unnamedExprs = unnamedExpressions.map(args(_)) + throw QueryCompilationErrors.invalidQueryAllParametersMustBeNamed(unnamedExprs) + } + } + + // Check all arguments for validity (args are already evaluated expressions/literals) + val allArgs = args.zipWithIndex.map { case (arg, idx) => + val name = if (idx < paramNames.length && paramNames(idx) != null) { + paramNames(idx) + } else { + s"_$idx" + } + (name, arg) + } + checkArgs(allArgs) + + // Single pass binding - args are already literals/evaluated expressions + val namedArgsMap = paramNames.zipWithIndex.collect { + case (name, index) if name != null => name -> args(index) + }.toMap + val positionalArgsMap = if (positionalParams.nonEmpty) { + val sortedPositions = positionalParams.toSeq.sorted + sortedPositions.zipWithIndex.collect { case (pos, index) if index < args.length => + pos -> args(index) + }.toMap + } else Map.empty[Int, Expression] + + bind(child) { Review Comment: can we do everything in one pass? Something like this ``` val namedArgsMap = if (paramNames.isEmpty) Map.empty else ... var hasNamedParameter = false bind(child) { case p @ NamedParameter(name) => if (namedArgsMap.isEmpty) throw QueryCompilationErrors.invalidQueryAllParametersMustBeNamed hasNamedParameter = true namedArgsMap.getOrElse(name, p) case PosParameter(pos) => if (hasNamedParameter) throw QueryCompilationErrors.invalidQueryMixedQueryParameters() ... } ``` -- 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: reviews-unsubscr...@spark.apache.org For queries about this service, please contact Infrastructure at: us...@infra.apache.org --------------------------------------------------------------------- To unsubscribe, e-mail: reviews-unsubscr...@spark.apache.org For additional commands, e-mail: reviews-h...@spark.apache.org