[
https://issues.apache.org/jira/browse/FLINK-40496?page=com.atlassian.jira.plugin.system.issuetabpanels:all-tabpanel
]
Arvid Heise updated FLINK-40496:
--------------------------------
Description:
h2. What happens
CREATE OR ALTER derives a materialized table's column changes differently from
ALTER ... AS and does not surface a query-driven column reorder to the
append-only validation. Reordering existing columns via CREATE OR ALTER is
therefore handled inconsistently:
* when the query text is unchanged (e.g. only a bare column list reorders the
columns), the reorder is silently applied - the stored column order is
rewritten with no error;
* when the query text changes, the same reorder is rejected.
The silent path leaves the stored schema in an order that no longer matches the
query. The refresh runs a positional INSERT INTO <table> <expandedQuery> with
no explicit column list, so the mismatch then miscompiles with a sink type
error, e.g. "Incompatible types for sink column ... at position ...".
Example - existing materialized table users_shops = (user_id, shop_id, ds,
order_cnt):
{code:sql}
CREATE OR ALTER MATERIALIZED TABLE users_shops (shop_id, user_id, ds, order_cnt)
AS SELECT user_id, shop_id, ds, COUNT(order_id) AS order_cnt FROM ...
{code}
The bare list reorders user_id/shop_id while the query is unchanged, so no
ModifyDefinitionQuery is produced and the append-only check is skipped: the
columns are silently reordered to (shop_id, user_id, ds, order_cnt).
h2. Fix
Derive the CREATE OR ALTER column diff from the query the same way ALTER ... AS
does, and apply the append-only column rules to every query-carrying alter
(CREATE OR ALTER and ALTER ... AS) regardless of whether the query text
changed. Reordering or retyping existing columns is then rejected consistently
across both statements.
was:
h2. What happens
When CREATE OR ALTER evolves a materialized table's query so a column is added
anywhere but the end of the projection, the stored schema does not follow the
query's column order: the new column is appended at the end and the
pre-existing columns keep their positions. ALTER MATERIALIZED TABLE ... AS
handles the same evolution correctly.
Given mt = (a, b, c, d) from SELECT a, b, c, d FROM t1:
{code:sql}
CREATE OR ALTER MATERIALIZED TABLE mt AS SELECT a, b, c, 42 AS mid, d FROM t1;
{code}
Expected stored schema: (a, b, c, mid, d) -- the query projection order.
Actual stored schema: (a, b, c, d, mid) -- mid appended at the end.
This passes all DDL-time validation, including
AlterMaterializedTableChangeOperation.validateChanges(); nothing rejects it.
The mismatch only surfaces later, when the refresh runs a positional INSERT
INTO mt <expandedQuery> (no column list): query position 4 (mid) binds to
stored position 4 (d), and planning fails with a sink type mismatch
(DynamicSinkUtils: "Incompatible types for sink column ... at position ...").
h2. Root cause
The two query-evolution paths use different diff methods, and only one diffs
column position:
* ALTER ... AS uses MaterializedTableUtils.buildSchemaTableChanges, which
appends new columns and calls applyPositionChanges for name-matched columns,
emitting modifyColumnPosition so existing columns flow around the appended one.
* CREATE OR ALTER uses MaterializedTableUtils.validateAndExtractColumnChanges
(via SqlCreateOrAlterMaterializedTableConverter.getSchemaTableChanges), which
appends new columns but never diffs position. A comment there states position
diffing is skipped on the assumption that reorders are handled on the ALTER MT
AS path; that path is not reached for CREATE OR ALTER.
By the time either method runs, the new schema's column order is the query
projection order (an explicit DDL column list only overrides types/comments in
place via MergeTableAsUtil.mergeColumns; the identifier-only form permutes
exactly the query's columns), so the position must be honored.
h2. Fix
Make CREATE OR ALTER diff column position like ALTER ... AS: in
validateAndExtractColumnChanges, emit modifyColumnPosition for name-matched
columns whose index changed. Position each old column by its rank among the
columns that survive into the new schema, so retained non-persisted columns
(which CREATE OR ALTER keeps but the query projection omits) do not skew the
comparison.
With the fix, CREATE OR ALTER matches ALTER ... AS: inserting a column before
the last column stores the correct order, and an insertion that would reorder
existing physical columns relative to each other is rejected up front by
validateChanges (the append-only guard) with a clear error, instead of silently
miscompiling at refresh time.
h2. Note
Making the refresh INSERT column-name-qualified would avoid the miscompile, but
the stored schema order would still disagree with the query, so it complements
rather than replaces this fix.
> CREATE OR ALTER MATERIALIZED TABLE applies column-change rules inconsistently
> with ALTER ... AS and can silently reorder existing columns
> -----------------------------------------------------------------------------------------------------------------------------------------
>
> Key: FLINK-40496
> URL: https://issues.apache.org/jira/browse/FLINK-40496
> Project: Flink
> Issue Type: Bug
> Components: Table SQL / Planner
> Affects Versions: 2.3.0
> Reporter: Arvid Heise
> Assignee: Arvid Heise
> Priority: Major
> Labels: pull-request-available
>
> h2. What happens
> CREATE OR ALTER derives a materialized table's column changes differently
> from ALTER ... AS and does not surface a query-driven column reorder to the
> append-only validation. Reordering existing columns via CREATE OR ALTER is
> therefore handled inconsistently:
> * when the query text is unchanged (e.g. only a bare column list reorders the
> columns), the reorder is silently applied - the stored column order is
> rewritten with no error;
> * when the query text changes, the same reorder is rejected.
> The silent path leaves the stored schema in an order that no longer matches
> the query. The refresh runs a positional INSERT INTO <table> <expandedQuery>
> with no explicit column list, so the mismatch then miscompiles with a sink
> type error, e.g. "Incompatible types for sink column ... at position ...".
> Example - existing materialized table users_shops = (user_id, shop_id, ds,
> order_cnt):
> {code:sql}
> CREATE OR ALTER MATERIALIZED TABLE users_shops (shop_id, user_id, ds,
> order_cnt)
> AS SELECT user_id, shop_id, ds, COUNT(order_id) AS order_cnt FROM ...
> {code}
> The bare list reorders user_id/shop_id while the query is unchanged, so no
> ModifyDefinitionQuery is produced and the append-only check is skipped: the
> columns are silently reordered to (shop_id, user_id, ds, order_cnt).
> h2. Fix
> Derive the CREATE OR ALTER column diff from the query the same way ALTER ...
> AS does, and apply the append-only column rules to every query-carrying alter
> (CREATE OR ALTER and ALTER ... AS) regardless of whether the query text
> changed. Reordering or retyping existing columns is then rejected
> consistently across both statements.
--
This message was sent by Atlassian Jira
(v8.20.10#820010)