Skip to content

[SPARK-48031][SQL] Support view schema evolution - #46267

Closed
srielau wants to merge 1 commit into
apache:masterfrom
srielau:SPARK-48031-view-evolution
Closed

[SPARK-48031][SQL] Support view schema evolution#46267
srielau wants to merge 1 commit into
apache:masterfrom
srielau:SPARK-48031-view-evolution

Conversation

@srielau

@srielau srielau commented Apr 28, 2024

Copy link
Copy Markdown
Contributor

What changes were proposed in this pull request?

Add the following syntax to CREATE VIEW... WITH SCHEMA ...:

CREATE [ OR REPLACE ] [ TEMPORARY ] VIEW [ IF NOT EXISTS ] view_name
    [ column_list ]
    [ schema_binding ] 
    [ COMMENT view_comment ]
    [ TBLPROPERTIES clause ]
    AS query

schema_binding
   WITH SCHEMA { BINDING | [TYPE] EVOLUTION | COMPENSATION }

column_list
   ( { column_alias [ COMMENT column_comment ] } [, ...] )

Allow changing of the schema binding in:

ALTER VIEW view_name  schema_binding

The semantic is:

Schema_binding
Optionally specifies how the view adapts to changes to the schema of the query due to changes in the underlying object definitions.
This clause is not supported for temporary views.

  • BINDING
    The view will become invalid if the query column-list changes except for the following conditions:
    The column-list includes a star clause, and there are additional columns. These additional columns are ignored.
    The type of one or more columns changed in a way that allows them to be safely cast to the types using implicit casting rules.

  • COMPENSATION
    The view will become invalid if the query column list changes except for the following conditions:
    The column-list includes a star clause, and there are additional columns. These additional columns are ignored.
    The type of one or more columns changed in a way that allows them to be cast using explicit cast rules.
    This is the default behavior.

  • TYPE EVOLUTION
    The view will adopt any changes to types in the query column list into is own definition when such a change is detected upon reference of the view.

  • EVOLUTION
    Behaves like TYPE EVOLUTION and also adopts changes in column names or added and dropped columns if the view does not include an explicit column list.
    The view will only be invalidated if the query cannot be parsed anymore, or the optional view column_list does not match the number of expressions in the query select-list anymore.

We also introduce a new SQL Config:
spark.sql.defaultViewSchemaBinding
Control the default behavior of views when the underlying schema changes.
Valid values are:

  • COMPENSATION - Any supported casts.
  • DISABLED - Disable the feature.

Why are the changes needed?

Schema changes are a frequent occurrence, especially when ingesting data.
In the course of it most frequently:

  • Types may need to be widened
  • Columns are added to tables
  • Fields are added to structs

The traditional SCHEMA BINDING behavior makes schema evolution very hard since it invalidates views agressively.
This causes erros, requiring user intervention.
Allowing views to be created to "roll with the punches" or tolerate changes in the underlying schema improves uptime.

Does this PR introduce any user-facing change?

Yes, this is a new feature with new grammar and a new config to influence its default.

How was this patch tested?

New tests are added

Was this patch authored or co-authored using generative AI tooling?

No

@github-actions github-actions Bot added the SQL label Apr 28, 2024
@srielau
srielau force-pushed the SPARK-48031-view-evolution branch 2 times, most recently from 3520d00 to aa89f73 Compare April 28, 2024 20:35
@github-actions github-actions Bot added the DOCS label Apr 28, 2024
@srielau
srielau force-pushed the SPARK-48031-view-evolution branch 5 times, most recently from 7ddc1e6 to b615e46 Compare May 1, 2024 02:41
Comment thread common/utils/src/main/resources/error/error-conditions.json Outdated
Comment thread common/utils/src/main/resources/error/error-conditions.json Outdated
@srielau srielau changed the title [WIP][SPARK-48031] view evolution [SPARK-48031] view evolution May 1, 2024
@srielau

srielau commented May 1, 2024

Copy link
Copy Markdown
Contributor Author

@cloud-fan @gengliangwang This is ready for an initial review.
Other than docs, the main open issue is NOT NULL behavior and COMMENTs.

@srielau
srielau force-pushed the SPARK-48031-view-evolution branch from 3d1f21f to 8996d76 Compare May 2, 2024 22:13
Comment thread common/utils/src/main/resources/error/error-conditions.json Outdated
Comment thread common/utils/src/main/resources/error/error-conditions.json Outdated
@srielau
srielau force-pushed the SPARK-48031-view-evolution branch 2 times, most recently from a731139 to 3b67105 Compare May 5, 2024 18:43
@srielau

srielau commented May 5, 2024

Copy link
Copy Markdown
Contributor Author

@cloud-fan @gengliangwang This is ready now. Please review.
Docs are included in this PR.

Comment thread sql/catalyst/src/main/scala/org/apache/spark/sql/internal/SQLConf.scala Outdated
Comment thread sql/catalyst/src/main/scala/org/apache/spark/sql/internal/SQLConf.scala Outdated
Comment thread sql/catalyst/src/main/scala/org/apache/spark/sql/catalyst/catalog/interface.scala Outdated
Comment thread sql/catalyst/src/main/scala/org/apache/spark/sql/catalyst/catalog/interface.scala Outdated
Comment thread sql/catalyst/src/main/scala/org/apache/spark/sql/catalyst/catalog/interface.scala Outdated
Comment thread sql/catalyst/src/main/scala/org/apache/spark/sql/catalyst/parser/AstBuilder.scala Outdated
@gengliangwang

Copy link
Copy Markdown
Member

BINDING
The view will become invalid if the query column-list changes except for the following conditions:
The column-list includes a star clause, and there are additional columns. These additional columns are ignored.
The type of one or more columns changed in a way that allows them to be safely cast to the types using implicit casting rules.
This is the default behavior.

@srielau The PR description seems wrong. The default is COMPENSATION, right?

@srielau
srielau force-pushed the SPARK-48031-view-evolution branch from 4166648 to 179f2f9 Compare May 8, 2024 18:37
@gengliangwang gengliangwang changed the title [SPARK-48031] view evolution [SPARK-48031][SQL] Support view schema evolution May 9, 2024
Comment thread common/utils/src/main/resources/error/error-conditions.json Outdated
Comment thread sql/catalyst/src/main/scala/org/apache/spark/sql/internal/SQLConf.scala Outdated
Comment thread docs/sql-ref-syntax-ddl-create-view.md Outdated
Comment thread docs/sql-ref-syntax-ddl-create-view.md Outdated
Comment thread sql/catalyst/src/main/scala/org/apache/spark/sql/catalyst/analysis/view.scala Outdated
Comment thread sql/catalyst/src/main/scala/org/apache/spark/sql/internal/SQLConf.scala Outdated
Comment thread sql/core/src/main/scala/org/apache/spark/sql/execution/SparkSqlParser.scala Outdated
Comment thread sql/core/src/main/scala/org/apache/spark/sql/execution/command/views.scala Outdated
Comment thread sql/core/src/main/scala/org/apache/spark/sql/execution/command/views.scala Outdated
Comment thread sql/core/src/main/scala/org/apache/spark/sql/execution/datasources/rules.scala Outdated
Comment thread sql/core/src/main/scala/org/apache/spark/sql/execution/datasources/rules.scala Outdated
Comment thread sql/core/src/test/resources/sql-tests/inputs/view-schema-binding.sql Outdated
Comment thread sql/core/src/test/resources/sql-tests/results/view-schema-binding.sql.out Outdated
@mridulm

mridulm commented May 9, 2024

Copy link
Copy Markdown
Contributor

+CC @shardulm94, @robreeves

@srielau

srielau commented May 9, 2024

Copy link
Copy Markdown
Contributor Author

@cloud-fan @gengliangwang I have addressed all comments (except the "override" on the, which intelij and Wenchen appear to be of different opinions).
The testsuite has been split up into:

  • Default behavior depending on config.
  • 1 file per mode.

I don't see a good way to have one file with 4 distinct modes since they have sufficient syntax and test differences that I think "spaghetti" is more readable than trying to maximize shared text.

Comment thread sql/core/src/main/scala/org/apache/spark/sql/execution/datasources/rules.scala Outdated
Comment thread sql/core/src/main/scala/org/apache/spark/sql/execution/datasources/rules.scala Outdated
Comment thread sql/core/src/main/scala/org/apache/spark/sql/execution/datasources/rules.scala Outdated
Comment thread sql/core/src/test/resources/sql-tests/results/view-schema-binding.sql.out Outdated
@srielau
srielau force-pushed the SPARK-48031-view-evolution branch from 1345224 to 2fdbe35 Compare May 13, 2024 11:42
@srielau

srielau commented May 13, 2024

Copy link
Copy Markdown
Contributor Author

@cloud-fan @gengliangwang All of @cloud-fan comments have been accepted and addressed.
Shall we merge it?

@cloud-fan

Copy link
Copy Markdown
Contributor

the k8s test failure is unrelated, thanks, merging to master!

import org.apache.spark.sql.catalyst.dsl.expressions._
val projectList = metadata.schema.map { field =>
UpCast(
Cast(

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Seems like this breaks non-ANSI build. Made a followup: #46614

HyukjinKwon added a commit that referenced this pull request May 16, 2024
### What changes were proposed in this pull request?

This PR is a followup of #46267 that uses ANSI-enabled cast in the tests. It intentionally uses ANSI-enabled cast in `castColToType` when you look up a view.

### Why are the changes needed?

In order to fix the scheduled CI build without ANSI:

- https://github.com/apache/spark/actions/runs/9072308206/job/24960016975
- https://github.com/apache/spark/actions/runs/9072308206/job/24960019187

```
[info] - look up view relation *** FAILED *** (72 milliseconds)
[info]   == FAIL: Plans do not match ===
[info]    'SubqueryAlias spark_catalog.db3.view1                                                                                                                                                                                                                                                                                                                                                                          'SubqueryAlias spark_catalog.db3.view1
[info]    +- View (`spark_catalog`.`db3`.`view1`, ['col1, 'col2, 'a, 'b])                                                                                                                                                                                                                                                                                                                                                 +- View (`spark_catalog`.`db3`.`view1`, ['col1, 'col2, 'a, 'b])
[info]       +- 'Project [cast(getviewcolumnbynameandordinal(`spark_catalog`.`db3`.`view1`, col1, 0, 1) as int) AS col1#0, cast(getviewcolumnbynameandordinal(`spark_catalog`.`db3`.`view1`, col2, 0, 1) as string) AS col2#0, cast(getviewcolumnbynameandordinal(`spark_catalog`.`db3`.`view1`, a, 0, 1) as int) AS a#0, cast(getviewcolumnbynameandordinal(`spark_catalog`.`db3`.`view1`, b, 0, 1) as string) AS b#0]      +- 'Project [cast(getviewcolumnbynameandordinal(`spark_catalog`.`db3`.`view1`, col1, 0, 1) as int) AS col1#0, cast(getviewcolumnbynameandordinal(`spark_catalog`.`db3`.`view1`, col2, 0, 1) as string) AS col2#0, cast(getviewcolumnbynameandordinal(`spark_catalog`.`db3`.`view1`, a, 0, 1) as int) AS a#0, cast(getviewcolumnbynameandordinal(`spark_catalog`.`db3`.`view1`, b, 0, 1) as string) AS b#0]
[info]          +- 'Project [*]                                                                                                                                                                                                                                                                                                                                                                                                 +- 'Project [*]
[info]             +- 'UnresolvedRelation [tbl1], [], false
```

```
[info] - look up view created before Spark 3.0 *** FAILED *** (452 milliseconds)
[info]   == FAIL: Plans do not match ===
[info]    'SubqueryAlias spark_catalog.db3.view2                                                                                                                                                                                                                                                                                                          'SubqueryAlias spark_catalog.db3.view2
[info]    +- View (`db3`.`view2`, ['col1, 'col2, 'a, 'b])                                                                                                                                                                                                                                                                                                 +- View (`db3`.`view2`, ['col1, 'col2, 'a, 'b])
[info]       +- 'Project [cast(getviewcolumnbynameandordinal(`db3`.`view2`, col1, 0, 1) as int) AS col1#0, cast(getviewcolumnbynameandordinal(`db3`.`view2`, col2, 0, 1) as string) AS col2#0, cast(getviewcolumnbynameandordinal(`db3`.`view2`, a, 0, 1) as int) AS a#0, cast(getviewcolumnbynameandordinal(`db3`.`view2`, b, 0, 1) as string) AS b#0]      +- 'Project [cast(getviewcolumnbynameandordinal(`db3`.`view2`, col1, 0, 1) as int) AS col1#0, cast(getviewcolumnbynameandordinal(`db3`.`view2`, col2, 0, 1) as string) AS col2#0, cast(getviewcolumnbynameandordinal(`db3`.`view2`, a, 0, 1) as int) AS a#0, cast(getviewcolumnbynameandordinal(`db3`.`view2`, b, 0, 1) as string) AS b#0]
[info]          +- 'Project [*]                                                                                                                                                                                                                                                                                                                                 +- 'Project [*]
[info]             +- 'UnresolvedRelation [tbl1], [], false                                                                                                                                                                                                                                                                                                        +- 'UnresolvedRelation [tbl1], [], false (PlanTest.scala:179)

```

### Does this PR introduce _any_ user-facing change?

No, the main change has not been released yet.

### How was this patch tested?

Manually ran the tests after ANSI disabled.

### Was this patch authored or co-authored using generative AI tooling?

No.

Closes #46614 from HyukjinKwon/SPARK-48031-followup.

Authored-by: Hyukjin Kwon <gurwls223@apache.org>
Signed-off-by: Hyukjin Kwon <gurwls223@apache.org>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

6 participants