dongjoon-hyun opened a new pull request, #59736:
URL: https://github.com/apache/spark/pull/59736

   ### What changes were proposed in this pull request?
   
   This PR makes `PipelinesHandler.blockUnsupportedSqlCommand`, the guard for 
`spark.sql(...)` in Spark Declarative Pipelines (SDP), reject three kinds of 
statements. The parser does not produce a `Command` for them, but they run a 
command or a procedure once they are analyzed.
   
   | Statement | Top node of the parsed plan | What happens during analysis |
   |---|---|---|
   | `EXECUTE IMMEDIATE '<sql>'` | `UnresolvedExecuteImmediate` | 
`ResolveExecuteImmediate` parses and analyzes `<sql>`, and a command payload 
then runs as `ExecuteImmediateCommand`. |
   | `CALL cat.ns.proc(...)` | `Call` (an `ExecutableDuringAnalysis`) | 
`InvokeProcedures` runs the procedure. |
   | A statement named by an `IDENTIFIER` clause that the parser does not build 
yet, e.g. `USE SCHEMA IDENTIFIER('a' \|\| 'b')`, `CREATE TABLE IDENTIFIER('a' 
\|\| 'b') (id INT)`, or `CREATE TEMPORARY VIEW IDENTIFIER('v') AS SELECT 1` | 
`PlanWithUnresolvedIdentifier` | `ResolveIdentifierClause` evaluates the clause 
and builds the actual command. |
   
   The guard now does the following.
   - It blocks `UnresolvedExecuteImmediate` and `ExecutableDuringAnalysis`, the 
trait that `Call` implements.
   - For a top-level `PlanWithUnresolvedIdentifier`, it builds the statement 
through the node's `planBuilder` with a placeholder name and checks that 
statement with the same allowlist and blocklist. As a result, `USE SCHEMA 
IDENTIFIER('a' || 'b')` is rejected as `SetNamespaceCommand`, like `USE SCHEMA 
IDENTIFIER('ab')`, while read-only forms such as `SHOW COLUMNS FROM 
IDENTIFIER(...)` and `TABLE IDENTIFIER(...)` are still allowed.
   
   Because the change is in `blockUnsupportedSqlCommand`, it covers both a 
plain SQL relation and the root SQL of a SQL-with-references `WITH_RELATIONS` 
(SPARK-57189).
   
   Like the existing checks, the new checks are applied to the top node of the 
parsed plan. Nested cases such as `WITH ... INSERT ...` are out of scope here. 
They are the top-node limitation discussed in 
https://github.com/apache/spark/pull/59425.
   
   ### Why are the changes needed?
   
   The guard checks only the class of the top node of the parsed plan. The 
statements above pass that check, and `session.sql` then analyzes them, which 
runs the command or the procedure. For example, the following line in a 
pipeline definition file creates a table while the pipeline is being 
registered, although `spark.sql("CREATE TABLE ...")` is rejected.
   
   ```python
   spark.sql("EXECUTE IMMEDIATE 'CREATE TABLE spark_catalog.default.t (id 
INT)'")
   ```
   
   Similarly, `spark.sql("USE SCHEMA IDENTIFIER('some_' || 'schema')")` runs 
`SetNamespaceCommand` although `USE SCHEMA some_schema` is rejected, and `CALL` 
runs a procedure of a configured `ProcedureCatalog`. Inside a flow function, 
the guard lets these statements pass as well.
   
   ### Does this PR introduce _any_ user-facing change?
   
   Yes. In an SDP pipeline definition, `spark.sql(...)` with `EXECUTE 
IMMEDIATE`, `CALL`, or a command named by such an `IDENTIFIER` clause now fails 
with errors like the following instead of running.
   
   ```
   [UNSUPPORTED_PIPELINE_SPARK_SQL_COMMAND] 'UnresolvedExecuteImmediate' is not 
supported in spark.sql("...") API in Spark Declarative Pipeline. SQLSTATE: 0A000
   [UNSUPPORTED_PIPELINE_SPARK_SQL_COMMAND] 'Call' is not supported in 
spark.sql("...") API in Spark Declarative Pipeline. SQLSTATE: 0A000
   [UNSUPPORTED_PIPELINE_SPARK_SQL_COMMAND] 'SetNamespaceCommand' is not 
supported in spark.sql("...") API in Spark Declarative Pipeline. SQLSTATE: 0A000
   ```
   
   This includes `EXECUTE IMMEDIATE` of a query such as `EXECUTE IMMEDIATE 
'SELECT 1'`, because its SQL string is parsed only during analysis. This is a 
bug fix for the SDP SQL guard, which has existed since Apache Spark 
[4.1.0](https://github.com/apache/spark/releases/tag/v4.1.0) (2025-12-11).
   
   ### How was this patch tested?
   
   Pass the CIs with the newly added test cases in `PythonPipelineSuite`.
   - `unsupportedSqlCommandList`: `EXECUTE IMMEDIATE`, `CALL`, `USE SCHEMA` 
with a non-literal `IDENTIFIER` clause, and `CREATE TEMPORARY VIEW` with a 
literal `IDENTIFIER` clause. Each is checked outside and inside a query 
function. Without the fix, these 8 test cases fail because the guard does not 
reject the statements. For example, outside a query function, `EXECUTE 
IMMEDIATE` creates the table, and `USE SCHEMA IDENTIFIER(...)` runs 
`SetNamespaceCommand` and fails with `SCHEMA_NOT_FOUND`.
   - `supportedSqlCommandList`: `SHOW COLUMNS FROM IDENTIFIER(...)` and `TABLE 
IDENTIFIER(...)` with a non-literal `IDENTIFIER` clause, to make sure that the 
new check does not block them.
   
   ```
   $ build/sbt 'connect/testOnly 
org.apache.spark.sql.connect.pipelines.PythonPipelineSuite -- -z "SQL command"'
   ```
   
   ### Was this patch authored or co-authored using generative AI tooling?
   
   Generated-by: Claude Opus 5.5


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