srielau commented on code in PR #53530:
URL: https://github.com/apache/spark/pull/53530#discussion_r2680060867


##########
sql/catalyst/src/main/scala/org/apache/spark/sql/catalyst/parser/AstBuilder.scala:
##########
@@ -6573,6 +6575,110 @@ class AstBuilder extends DataTypeAstBuilder
     )
   }
 
+  /**
+   * Create a [[DeclareCursor]] command wrapped in SingleStatement.
+   *
+   * For example:
+   * {{{
+   *   DECLARE cursor_name CURSOR FOR SELECT * FROM table;
+   * }}}
+   */
+  override def visitDeclareCursorStatement(
+      ctx: DeclareCursorStatementContext): LogicalPlan = withOrigin(ctx) {
+    if (!conf.getConf(SQLConf.SQL_SCRIPTING_CURSOR_ENABLED)) {
+      throw SqlScriptingErrors.cursorNotSupported(CurrentOrigin.get)
+    }
+
+    val cursorName = getIdentifierText(ctx.name)
+    // Extract original SQL text to preserve parameter markers
+    val queryText = getOriginalText(ctx.query())
+
+    val asensitive = ctx.ASENSITIVE() != null || ctx.INSENSITIVE() != null
+    SingleStatement(DeclareCursor(cursorName, queryText, asensitive))
+  }
+
+  /**
+   * Create an [[OpenCursor]] command wrapped in SingleStatement.
+   *
+   * For example:
+   * {{{
+   *   OPEN cursor_name;
+   *   OPEN cursor_name USING expr1, expr2;
+   *   OPEN cursor_name USING (expr1 AS param1, expr2 AS param2);
+   * }}}
+   */
+  override def visitOpenCursorStatement(
+      ctx: OpenCursorStatementContext): LogicalPlan = withOrigin(ctx) {
+    if (!conf.getConf(SQLConf.SQL_SCRIPTING_CURSOR_ENABLED)) {
+      throw SqlScriptingErrors.cursorNotSupported(CurrentOrigin.get)
+    }
+
+    // Use visitMultipartIdentifier to properly handle IDENTIFIER('name')
+    val nameParts = visitMultipartIdentifier(ctx.multipartIdentifier())
+
+    // Parse optional USING clause parameters
+    // Extract both expressions and their names (if aliased)
+    val (args, paramNames) = Option(ctx.params).map { params =>
+      params.namedExpression().asScala.toSeq.map(visitNamedExpression).map {
+        case alias: Alias => (alias.child, alias.name)
+        case expr => (expr, "")  // Empty string for positional parameter
+      }.unzip
+    }.getOrElse((Seq.empty, Seq.empty))
+
+    SingleStatement(OpenCursor(nameParts, args, paramNames))
+  }
+
+  /**
+   * Create a [[FetchCursor]] command wrapped in SingleStatement.
+   *
+   * For example:
+   * {{{
+   *   FETCH cursor_name INTO var1, var2;
+   * }}}
+   */
+  override def visitFetchCursorStatement(
+      ctx: FetchCursorStatementContext): LogicalPlan = withOrigin(ctx) {
+    if (!conf.getConf(SQLConf.SQL_SCRIPTING_CURSOR_ENABLED)) {
+      throw SqlScriptingErrors.cursorNotSupported(CurrentOrigin.get)
+    }
+
+    // Use visitMultipartIdentifier to properly handle IDENTIFIER('name')
+    val nameParts = visitMultipartIdentifier(ctx.multipartIdentifier())
+
+    val targetVariables = ctx.identifierReference().asScala.map { varIdent =>
+      val varName = if (varIdent.expression() != null) {
+        // IDENTIFIER(expression) case - not supported for variables

Review Comment:
   I've added IDENTIFIER support



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