dtenedor opened a new pull request, #59681:
URL: https://github.com/apache/spark/pull/59681

   ### What changes were proposed in this pull request?
   
   This PR allows VALUES lists (inline tables) to contain expressions that are 
not constant-foldable, for example:
   - nondeterministic functions such as `rand()` and `uuid()`;
   - Scala/Java UDFs;
   - higher-order functions with lambdas;
   - `raise_error()`;
   - uncorrelated scalar subqueries.
   
   Such a VALUES list is planned as an `Expand` over a `OneRowRelation`, with 
one projection per row, so its rows are evaluated by the existing `ExpandExec` 
when the query runs.
   
   VALUES lists that are valid today keep exactly their current behavior: they 
go through the same validation, produce the same `LocalRelation` or 
`ResolvedInlineTable`, and are evaluated by the unchanged `EvalInlineTables` 
rule. The new code path only applies to VALUES lists that the current 
validation rejects.
   
   **Analysis**
   - `EvaluateUnresolvedInlineTable.validateInputEvaluable` accepts every 
expression it accepts today, plus expressions that can be evaluated by 
themselves when the query runs. It still rejects the following with the 
existing `INVALID_INLINE_TABLE.CANNOT_EVALUATE_EXPRESSION_IN_INLINE_TABLE` 
error:
     - aggregate, window, and generator functions;
     - references to columns of an outer query, e.g. `LATERAL (VALUES (t.a))`;
     - subqueries other than uncorrelated scalar subqueries, such as `EXISTS` 
and `IN`;
     - unevaluable expressions that would have to be rewritten into separate 
operators, such as SQL UDFs and Python UDFs.
   - If every expression of a VALUES list is foldable or contains a 
CURRENT_LIKE expression, which is what the current validation requires, the 
list takes the current code path unchanged. Otherwise, 
`EvaluateUnresolvedInlineTable` turns it into `Expand(rows, output, 
OneRowRelation())`:
     - Foldable cells are evaluated into literals during analysis, so 
evaluation errors in constant cells are still reported during analysis.
     - CURRENT_LIKE cells are replaced with literals at the end of the analysis 
phase, like today.
   - `ResolveSubquery` now resolves subqueries in `UnresolvedInlineTable`.
   - The single-pass resolver throws `ExplicitlyUnsupportedResolverFeature` for 
scalar subqueries in VALUES lists, and for VALUES lists that become an 
`Expand`. These queries fall back to the fixed-point analyzer.
   
   **Execution**
   - No new operators. `ExpandExec` applies the projections to the single row 
of its child, so all the rows of the VALUES list are evaluated in one task, 
with whole-stage codegen.
   - Each projection is evaluated once, for partition 0. As a result, `VALUES 
(rand(5)), (rand(5))` returns the same values as `SELECT rand(5) UNION ALL 
SELECT rand(5)`.
   - Scalar subqueries are planned and executed by `PlanSubqueries` and 
`PlanAdaptiveSubqueries` before `ExpandExec` runs.
   - Like in a `SELECT` list, `ColumnPruning` removes the expressions of 
columns that are not read.
   - `ExpandExec` fix: the generated code evaluated a column once per input row 
when the column had equal expressions in all projections, even if they were 
nondeterministic. For example, a nondeterministic UDF called the same way in 
every row of a VALUES list returned the same value in every row, unlike the 
interpreted path. Now only deterministic columns are shared.
   
   **Configuration**
   - New internal conf `spark.sql.inlineTable.nonFoldableExpressions.enabled`, 
default `true`. Setting it to `false` restores the previous behavior.
   
   **References to other DBMSes**
   - These engines accept general expressions in VALUES lists, including scalar 
subqueries: PostgreSQL, SQL Server, Oracle 23ai, MySQL 8.0.19+ and SQLite. 
BigQuery documents scalar subqueries in `INSERT ... VALUES`.
   - Snowflake and Hive keep restrictions similar to Spark's current behavior.
   
   ### Why are the changes needed?
   
   Today, every expression in a VALUES list must be constant-foldable, with the 
exception of expressions like `current_timestamp()`, because the analyzer 
evaluates the rows on the driver. So queries such as the following fail:
   - `INSERT INTO t VALUES (uuid(), ...)`
   - `SELECT * FROM VALUES (rand())`
   - `INSERT INTO t VALUES ((SELECT max(id) FROM t) + 1, ...)`
   
   Users have to rewrite them as `INSERT ... SELECT` or as a `UNION ALL` of 
`SELECT`s. Most other SQL engines accept these queries.
   
   SPARK-28291 tracks UDFs in VALUES lists. This PR supports Scala/Java UDFs 
there, but not SQL or Python UDFs.
   
   ### Does this PR introduce _any_ user-facing change?
   
   Yes. Queries that failed before now succeed:
   
   ```sql
   SELECT * FROM VALUES ('one', rand(5)), ('two', 3.0D) AS data(a, b);
   -- Before: [INVALID_INLINE_TABLE.CANNOT_EVALUATE_EXPRESSION_IN_INLINE_TABLE]
   --         Cannot evaluate the expression "rand(5)" in inline table 
definition.
   -- After:
   -- one  0.02390696427502892
   -- two  3.0
   
   INSERT INTO t VALUES ((SELECT max(id) FROM t) + 1);
   -- Before: 
[UNSUPPORTED_SUBQUERY_EXPRESSION_CATEGORY.SCALAR_SUBQUERY_IN_VALUES]
   --         Scalar subqueries in the VALUES clause.
   -- After: inserts the next id.
   ```
   
   Expressions that still can't be evaluated in a VALUES list fail with 
`INVALID_INLINE_TABLE.CANNOT_EVALUATE_EXPRESSION_IN_INLINE_TABLE`. An inline 
table with non-foldable expressions shows up as an `Expand` over a 
`OneRowRelation` in plans. VALUES lists that are valid today are not affected.
   
   ### How was this patch tested?
   
   New tests:
   - `InlineTableNonFoldableExpressionsSuite` (22 tests), covering:
     - evaluation on executors, in a single task;
     - constant lists, which remain a `LocalTableScan`;
     - lists that are valid today: the same analyzed plans and results with the 
conf on and off, CURRENT_LIKE expressions inside a Scala UDF still evaluated on 
the driver, and evaluation errors next to CURRENT_LIKE expressions still 
reported by the optimizer;
     - nondeterministic semantics: a distinct `uuid()` per row, `rand(5)` 
matching `SELECT rand(5)`, stable values when a plan is re-executed, and equal 
nondeterministic UDF calls evaluated separately for every row, with whole-stage 
codegen on and off;
     - `current_timestamp()` evaluated once per query;
     - type coercion and nullability;
     - uncorrelated scalar subqueries, including `INSERT ... VALUES` that reads 
its target table;
     - rejected cases: `LATERAL` outer references, `EXISTS`, `IN`, aggregate, 
window and generator functions, SQL UDFs, and Python UDFs;
     - runtime errors (`raise_error`), and unread columns that are not 
evaluated;
     - `INSERT` with `DEFAULT` and `uuid()`;
     - DataFrame self-joins;
     - whole-stage codegen and AQE on and off;
     - SQL metrics;
     - a 1000-row list;
     - the conf set to `false`.
   - `ResolveInlineTablesSuite`, covering:
     - validation;
     - the `Expand` that replaces lists with non-foldable expressions, with 
constant cells folded;
     - lists without non-foldable expressions resolving to the same plans with 
the conf on and off;
     - aliased (`DEFAULT`) cells;
     - CURRENT_LIKE expressions;
     - analysis-time errors in constant cells;
     - `CheckAnalysis` of nondeterministic expressions.
   - New queries in `inline-table.sql`. The `rand(5)` cases in 
`inline-table.sql` and `udf/udf-inline-table.sql` now succeed.
   - `SQLQuerySuite`: the SPARK-49659 test runs with the conf set to `false` 
and checks that the query now works with the conf on.
   
   Regenerated `scripting/randomly_generated_scripts.sql`:
   - 22 scripts used to fail with `SCALAR_SUBQUERY_IN_VALUES` and exit through 
their `EXIT HANDLER`. They contain `INSERT ... VALUES` statements with scalar 
subqueries, and they now run to completion.
   - These scripts share tables, so the data they now insert changes the 
control flow of 6 later scripts:
     - 3 now reach `UPDATE` statements, which are unsupported for these Parquet 
tables.
     - 3 no longer reach such statements.
   - I verified every changed outcome by running the file with all `EXIT 
HANDLER`s removed, once with the conf on and once with it off.
   
   Existing suites:
   - `Expand` execution: `WholeStageCodegenSuite`, `PlannerSuite`, 
`AdaptivePartialAggregationSuite`, `DatasetUnpivotSuite`, 
`DataFrameAggregateSuite`, and the `GROUPING SETS` and UNPIVOT golden files.
   - Catalyst analyzer suites, including `AnalysisSuite`, `AnalysisErrorSuite`, 
`ResolveSubquerySuite` and `PlanParserSuite`.
   - `InlineTableParsingImprovementsSuite`, `ResolveDefaultColumnsSuite`, the 
`*InsertSuite`s and the `*SubquerySuite`s.
   - The single-pass resolver suites: `ResolverGuardSuite`, 
`HybridAnalyzerSuite`, the `*ResolverSuite`s, `ResolutionValidatorSuite`, 
`ExplicitlyUnsupportedResolverFeatureSuite`, `DataFrameAnalyzerTestGapsSuite`.
   - The `SQLQueryTestSuite` golden files for inline tables, CTEs, 
`create_view`, `insert`, session variables and scripting.
   
   ### Was this patch authored or co-authored using generative AI tooling?
   
   Generated-by: Cursor (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