This is an automated email from the ASF dual-hosted git repository.

gaogaotiantian pushed a commit to branch branch-4.x
in repository https://gitbox.apache.org/repos/asf/spark.git


The following commit(s) were added to refs/heads/branch-4.x by this push:
     new 4f540c08607d [SPARK-60159][PYTHON][TEST] Add tests for string and 
DataFrame keyword arguments in spark.sql formatting
4f540c08607d is described below

commit 4f540c08607d32d8d6512910074a8408b799659c
Author: Max <[email protected]>
AuthorDate: Sun Oct 11 01:42:19 2026 -0700

    [SPARK-60159][PYTHON][TEST] Add tests for string and DataFrame keyword 
arguments in spark.sql formatting
    
    ### What changes were proposed in this pull request?
    
    This PR adds tests to `SQLTestsMixin` in 
`python/pyspark/sql/tests/test_sql.py` for code paths of `spark.sql()` 
keyword-argument formatting that were not covered. Since the mixin is shared by 
`test_parity_sql.py`, the tests run in both classic and Spark Connect:
    
    - `test_kwargs_string_literal`: string values round-trip unchanged, 
including single and double quotes, backslashes (e.g. `C:\new`, a trailing 
`\`), `\'`, control characters, an injection-like value (`x' OR '1'='1`), and 
braces
    - `test_kwargs_dataframe_referenced_twice`:
      - the same DataFrame referenced twice by one name reuses one view
      - the same DataFrame passed under two names reuses one view
      - different DataFrames get different views
    
    ### Why are the changes needed?
    
    To improve test coverage of 
`pyspark.sql.connect.sql_formatter.SQLStringFormatter`. The string-literal 
branch and the view-reuse branch had no tests, so regressions in 
quoting/escaping or in view naming would not be caught.
    
    ### Does this PR introduce _any_ user-facing change?
    
    No. Test-only change.
    
    ### How was this patch tested?
    
    Added unit tests in `python/pyspark/sql/tests/test_sql.py`, run in classic 
and Spark Connect:
    
    ```
    python/run-tests --python-executables=python --testnames 
"pyspark.sql.tests.test_sql,pyspark.sql.tests.connect.test_parity_sql"
    ```
    
    Ran the Connect parity tests with coverage before and after the change:
    
    ```
    python/run-tests-with-coverage --python-executables=python --testnames 
pyspark.sql.tests.connect.test_parity_sql
    ```
    
    Coverage of `pyspark/sql/connect/sql_formatter.py`:
    
    | | Stmts | Miss | BrPart | Cover |
    |---|---|---|---|---|
    | Before | 34 | 5 | 3 | 78% |
    | After | 34 | 2 | 1 | 93% |
    
    The remaining uncovered lines are the no-op `clear()` and the error for a 
non-plain column reference. The latter raises `PySparkValueError` without an 
error class in Spark Connect, unlike classic 
(`VALUE_NOT_PLAIN_COLUMN_REFERENCE`), so it is left out of this test-only PR.
    
    All tests pass. `dev/reformat-python` and `dev/lint-python` pass.
    
    ### Was this patch authored or co-authored using generative AI tooling?
    
    Generated-by: Claude Opus 5.5 (Anthropic), used as a pair-programming 
assistant. The author reviewed every line, ran the tests, and verified the 
coverage results locally.
    
    Closes #59361 from LearnFirst/SPARK-60159.
    
    Authored-by: Max <[email protected]>
    Signed-off-by: Tian Gao <[email protected]>
    (cherry picked from commit e81f9af4d7eb45d922cb4b6fb87eb2a2d1ffe9c6)
    Signed-off-by: Tian Gao <[email protected]>
---
 python/pyspark/sql/tests/test_sql.py | 52 ++++++++++++++++++++++++++++++++++++
 1 file changed, 52 insertions(+)

diff --git a/python/pyspark/sql/tests/test_sql.py 
b/python/pyspark/sql/tests/test_sql.py
index 1e7a3898e7b5..4c93d1fcd4fd 100644
--- a/python/pyspark/sql/tests/test_sql.py
+++ b/python/pyspark/sql/tests/test_sql.py
@@ -113,6 +113,58 @@ class SQLTestsMixin:
         self.assertEqual(df1.take(1), [Row(id=5)])
         self.assertEqual(df1.tail(1), [Row(id=8)])
 
+    def test_kwargs_string_literal(self):
+        # String values are converted to SQL string literals with quotes and
+        # backslashes escaped, so they round-trip unchanged.
+        for s in [
+            "abc",
+            "",
+            "it's",
+            'say "hi"',
+            "back\\slash",
+            "C:\\new",
+            "end\\",
+            "\\'",
+            "x' OR '1'='1",
+            "line1\nline2\ttab",
+            "{not_a_field}",
+        ]:
+            with self.subTest(s=s):
+                self.assertEqual(self.spark.sql("SELECT {s} AS v", 
s=s).first(), Row(v=s))
+
+        df = self.spark.sql(
+            "SELECT * FROM VALUES ('a'), ('b'), ('it''s') AS t(name) WHERE 
name = {name}",
+            name="it's",
+        )
+        self.assertEqual(df.collect(), [Row(name="it's")])
+
+    def test_kwargs_dataframe_referenced_twice(self):
+        df = self.spark.range(3)
+        expected = [Row(a=0, b=0), Row(a=1, b=1), Row(a=2, b=2)]
+
+        # The same DataFrame referenced twice by the same name.
+        df1 = self.spark.sql(
+            "SELECT l.id AS a, r.id AS b FROM {df} l JOIN {df} r ON l.id = 
r.id ORDER BY a",
+            df=df,
+        )
+        self.assertEqual(df1.collect(), expected)
+
+        # The same DataFrame passed under two different names.
+        df2 = self.spark.sql(
+            "SELECT l.id AS a, r.id AS b FROM {x} l JOIN {y} r ON l.id = r.id 
ORDER BY a",
+            x=df,
+            y=df,
+        )
+        self.assertEqual(df2.collect(), expected)
+
+        # Different DataFrames must not share a name.
+        df3 = self.spark.sql(
+            "SELECT l.id AS a, r.id AS b FROM {x} l JOIN {y} r ON l.id + 1 = 
r.id ORDER BY a",
+            x=df,
+            y=self.spark.range(1, 3),
+        )
+        self.assertEqual(df3.collect(), [Row(a=0, b=1), Row(a=1, b=2)])
+
     def test_nested_view(self):
         with self.temp_view("v1", "v2", "v3", "v4"):
             self.spark.range(10).createOrReplaceTempView("v1")


---------------------------------------------------------------------
To unsubscribe, e-mail: [email protected]
For additional commands, e-mail: [email protected]

Reply via email to