Skip to content

[fix](mtmv) Reanalyze partition expressions using MV output columns - #70289

Open
yujun777 wants to merge 1 commit into
apache:masterfrom
yujun777:codex/fix-mtmv-partition-reanalysis
Open

yujun777 wants to merge 1 commit into
apache:masterfrom
yujun777:codex/fix-mtmv-partition-reanalysis

Conversation

@yujun777

@yujun777 yujun777 commented Nov 5, 2026 •

Copy link
Copy Markdown
Contributor

Background

Partitioned asynchronous materialized views retain both their query output partition column and the base-table expression inferred from partition lineage. A base-table schema change causes refresh to reanalyze the stored MV query and validate its compatibility.

What problem does this PR solve?

Issue Number: None
Related PR: None
PR Type: Bugfix

Problem Summary:

After a base-table schema change, refreshing an MV whose date partition output has an alias can fail with partition column can not find from sql select column. Reanalysis parses the stored base-table expression and incorrectly searches for its base column in the query outputs. Render the partition expression using the MV output column, reusing the existing SHOW CREATE expression service, before parsing it again. Existing MVs benefit without metadata migration or changes to schema compatibility checks.

Details

  • Frequency: Reproducible when schema-change reanalysis encounters a partition expression whose base-column name differs from its MV output alias.
  • Root cause: The persisted lineage expression belongs to the base-table namespace, while partition definition analysis resolves names against query outputs.
  • Why existing tests missed it: The existing reanalysis coverage did not exercise the mismatch between stored base-column names and MV output aliases after VARCHAR widening. The new test covers a single source and an aggregated UNION ALL, with implicit and explicit expression partition definitions.
  • Fix: Reuse MTMVPartitionExprFactory.getExprService(expr).toSql(mvPartitionInfo) to reconstruct the analysis input. Do not mutate stored lineage or bypass partition/type compatibility checks.
  • Other paths and branches: SHOW CREATE already uses this renderer; null-expression partition definitions remain unchanged. This PR targets master only.

Testing

  • ./build.sh --fe: passed, including FE Checkstyle.
  • ./run-fe-ut.sh --run MTMVPartitionReanalysisTest,MTMVPlanUtilTest: 29 tests passed, with no failures or skips.
  • The four new parameterized cases cover single-source and aggregated UNION ALL queries, implicit and explicit date-trunc partition definitions, actual VARCHAR widening, refresh-time compatibility validation, and unchanged stored lineage.
  • Reverting the production fix makes all four new cases fail with partition column can not find from sql select column; with the fix all four pass.
  • Local review: no blocking findings. Service-level refresh execution and deployment validation were not run locally.

Release note

Fix asynchronous materialized view refresh failures after base-table schema changes when a date partition expression uses a query output alias.

Check List (For Author)

  • Behavior changed: Yes. Compatible schema changes no longer cause a spurious partition-column lookup failure.
  • Does this need documentation: No
  • Pre-submission status: Partially verified: build and focused FE tests pass; end-to-end refresh execution remains for CI.

### What problem does this PR solve?

Issue Number: None

Related PR: None

Problem Summary: Refresh-time schema validation parses a stored base-table partition expression as if it referred to MV query outputs. When a date-trunc output has an alias, reanalysis fails to find the base column in those outputs. Reuse the SHOW CREATE expression renderer to reconstruct the expression using the MV output column, preserving the stored lineage and existing compatibility checks.

### Release note

Fix asynchronous materialized view refresh failures after base-table schema changes when a date partition expression uses a query output alias.

### Check List (For Author)

- Test: Unit Test: MTMVPartitionReanalysisTest and MTMVPlanUtilTest (29 passed); FE build including Checkstyle passed. End-to-end refresh execution was not run locally.
- Behavior changed: Yes, compatible schema changes no longer cause a spurious partition-column lookup failure.
- Does this need documentation: No
@yujun777
yujun777 requested a review from morrySnow as a code owner November 5, 2026 10:38
@hello-stephen

Copy link
Copy Markdown
Contributor

Thank you for your contribution to Apache Doris.
Don't know what should be done next? See How to process your PR.

Please clearly describe your PR:

  1. What problem was fixed (it's best to include specific error reporting information). How it was fixed.
  2. Which behaviors were modified. What was the previous behavior, what is it now, why was it modified, and what possible impacts might there be.
  3. What features were added. Why was this function added?
  4. Which code was refactored and why was this part of the code refactored?
  5. Which functions were optimized and what is the difference before and after the optimization?

@yujun777

yujun777 commented Nov 5, 2026

Copy link
Copy Markdown
Contributor Author

run buildall

@yujun777

yujun777 commented Nov 5, 2026

Copy link
Copy Markdown
Contributor Author

/review

@github-actions github-actions Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Static review of PR #70289 at 2e57ef1 found three P2 regressions in partition-expression reconstruction: WEEK/QUARTER outputs gain an unsupported nested truncation, dotted output aliases fail name lookup, and embedded backticks produce invalid expression SQL. Each inline example retains the ordinary base column in the SELECT list, so the previous reanalysis path succeeds. These are distinct introduced failures; please address them before merging. No P0/P1 finding or existing blocking comment was identified. No additional user focus was supplied.

Critical checkpoint conclusions:

  • Goal and evidence: output-namespace reconstruction fixes the intended ordinary DAY alias mismatch, but the three supported cases above still regress. The four added parameterized cases exercise the real refresh compatibility gate, with single-source/aggregate-over-UNION ALL and implicit/explicit definitions.
  • Scope and reuse: the production change is small and reuses the existing SHOW CREATE expression service; its assumptions need the corrections described inline.
  • Concurrency and locking: refresh reanalysis still runs under the existing base-table read locks acquired in ID order. New objects are local; no new lock, shared mutation, external IO or journal wait was added.
  • Lifecycle and initialization: reanalysis builds separate analysis objects and does not mutate stored lineage. No new long-lived resource, callback or static-initialization dependency was introduced.
  • Configuration: no configuration was added or changed. Unicode naming is already enabled by default, making the embedded-backtick example reachable with defaults.
  • Compatibility: serialized fields, storage formats and function signatures are unchanged; legacy single-PCT adaptation remains. Behavioral compatibility of existing MVs is affected by the three findings.
  • Thrift/Protobuf: no definitions changed; branch field-ID checks are inapplicable.
  • Parallel paths: both analyzeQueryWithSql overloads, refresh-time schema validation, DDL-triggered invalidation and IVM property validation share the changed implementation. Null-expression and self-managed paths are unchanged.
  • Conditional checks and errors: the existing non-null expression guard remains appropriate. Factory/parser/analysis errors still propagate through UserException/JobException; the findings cause valid definitions to take those failure paths.
  • Test coverage: the new tests meaningfully cover DAY aliases and unchanged persisted lineage. They omit WEEK/QUARTER and quoted-identifier cases. Their VARCHAR widening uses synchronous light schema change; no new timing race was established. Existing tests cover unusable/changed partition lineage rejection. Full task execution, returned rows and the additional failing cases were not executed in this static review.
  • Expected results: no generated result files changed. Assertions and setup were inspected; author-reported build/test results were not independently executed.
  • Observability: existing task warnings and DDL invalidation logs include the MV/task or base-table context and failure cause; no additional instrumentation is required by this local change.
  • Transactions and persistence: no EditLog or replay logic changed, and existing column/PCT/time-unit compatibility checks remain after reanalysis. No new persistence or failover obligation was introduced.
  • Writes, atomicity and crashes: no write protocol changed. Visibility versions, delete bitmaps and refresh transaction atomicity are untouched; validation failures still stop refresh.
  • FE/BE propagation: no transmitted variable or wire contract changed.
  • Performance: the additional short-lived service and expression string occur per reanalysis, with no new row/partition loop or asymptotic cost. No benchmark was run.
  • Other risks: explicit MV column lists, multi-PCT lineage, factory restrictions and legacy metadata were traced without another introduced issue. Same-name base/output backtick serialization was already broken; the separate alias example in the inline finding was previously working.

Both changed files, direct callers, analysis/normalization and compatibility checks were covered. All candidate and risk dispositions are recorded in the review ledger; no unresolved static-review question remains. Review convergence and round count are reported with this delivery. Validation was static only: no builds, tests, service execution, source edits or submodule initialization.

Review converged after 2 round(s).

// The stored expression references base-table columns; reanalysis starts from MV output columns.
mtmvPartitionDefinition.setFunctionCallExpression(new NereidsParser().parseExpression(
expr.accept(ExprToSqlVisitor.INSTANCE, ToSqlParams.WITH_TABLE)));
MTMVPartitionExprFactory.getExprService(expr).toSql(mvPartitionInfo)));

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

[P2] Avoid adding an unmergeable WEEK/QUARTER truncation

For a range-partitioned DATETIME source with day-sized partitions, this implicit definition is supported:

PARTITION BY(q) AS
SELECT dt, date_trunc(dt, 'week') AS q, v FROM src

The stored expression is date_trunc(dt, 'week'); because raw dt is also projected, the previous reanalysis resolves it and succeeds. This renderer instead supplies date_trunc(q, 'week'), producing this lineage:

Project(date_trunc(q, 'week'))
  Project(dt, q = date_trunc(dt, 'week'), v)
    Scan(src)

PartitionIncrementMaintainer expands that to two nested truncations. MergeDateTrunc explicitly skips WEEK and QUARTER, including identical units, and checkPartition rejects expressions containing more than one DateTrunc. Consequently a compatible VARCHAR widening now makes refresh reanalysis fail for an existing MV. Preserve inferred-expression semantics or normalize these identical-unit pairs safely, and cover both units; all four new cases use DAY.

// The stored expression references base-table columns; reanalysis starts from MV output columns.
mtmvPartitionDefinition.setFunctionCallExpression(new NereidsParser().parseExpression(
expr.accept(ExprToSqlVisitor.INSTANCE, ToSqlParams.WITH_TABLE)));
MTMVPartitionExprFactory.getExprService(expr).toSql(mvPartitionInfo)));

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

[P2] Preserve the raw name of dotted partition outputs

A supported implicit partition definition can use a dotted output alias:

PARTITION BY(`q.day`) AS
SELECT dt, date_trunc(dt, 'day') AS `q.day`, v FROM src

Creation stores raw q.day as partitionCol and infers the base expression date_trunc(dt, 'day'). Keeping raw dt in the output lets the previous reanalysis succeed. The new renderer emits a function over the quoted alias, so MTMVPartitionDefinition.analyzeAndTransferToMTMVPartitionInfo now obtains the name through UnboundSlot.getName(). That method adds literal backticks around any name part containing a period. getRelatedTableInfos then compares the backtick-bearing string against the raw output name and fails with partition column can not find from sql select column. Preserve the raw single identifier part in this reconstruction path and add an implicit dotted-alias reanalysis case. This fails for DAY independently of nested-truncation normalization.

// The stored expression references base-table columns; reanalysis starts from MV output columns.
mtmvPartitionDefinition.setFunctionCallExpression(new NereidsParser().parseExpression(
expr.accept(ExprToSqlVisitor.INSTANCE, ToSqlParams.WITH_TABLE)));
MTMVPartitionExprFactory.getExprService(expr).toSql(mvPartitionInfo)));

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

[P2] Escape embedded backticks before reparsing the output alias

The reused MTMVPartitionExprDateTrunc.toSql wraps partitionCol in backticks without escaping its contents. Embedded backticks are legal with the default Unicode naming setting, so this implicit definition can be created:

PARTITION BY(`q``day`) AS
SELECT dt, date_trunc(dt, 'day') AS `q``day`, v FROM src

The old path reparses the stored expression over ordinary dt, which is also a query output, and succeeds. The new path interpolates the raw alias into the SQL text, producing malformed expression SQL:

date_trunc(`q`day`, 'day')

parseExpression now fails before compatibility validation, preventing schema-change refresh for this existing MV. Quote the identifier with the existing escaping utility, or build the unbound expression directly. This is separate from dotted-name handling: correcting UnboundSlot.getName() does not repair malformed SQL, and escaping embedded backticks does not fix the dotted-name lookup mismatch.

@hello-stephen

Copy link
Copy Markdown
Contributor
TPC-H: Total hot run time: 28525 ms
machine: 'aliyun_ecs.c7a.8xlarge_32C64G'
scripts: https://github.com/apache/doris/tree/master/tools/tpch-tools
Tpch sf100 test result on commit 2e57ef18210c32cea99f6843fe317399f9e47b82, data reload: false

------ Round 1 ----------------------------------
============================================
q1	17573	5168	5041	5041
q2	2144	365	248	248
q3	10181	1407	809	809
q4	4734	468	344	344
q5	7902	824	535	535
q6	220	181	149	149
q7	783	810	592	592
q8	10312	1357	1171	1171
q9	5693	4225	4220	4220
q10	6884	1311	1045	1045
q11	440	244	237	237
q12	686	396	281	281
q13	18191	3543	3015	3015
q14	271	267	243	243
q15	q16	726	728	638	638
q17	1217	773	597	597
q18	7074	6172	6136	6136
q19	1134	966	606	606
q20	392	343	228	228
q21	5241	2146	2275	2146
q22	362	307	244	244
Total cold run time: 102160 ms
Total hot run time: 28525 ms

----- Round 2, with runtime_filter_mode=off -----
============================================
q1	5631	5605	5597	5597
q2	710	574	537	537
q3	5234	5428	4940	4940
q4	2011	2153	1545	1545
q5	5290	5172	5168	5168
q6	231	184	143	143
q7	2109	1904	1703	1703
q8	2839	2490	2550	2490
q9	7594	7547	7534	7534
q10	3668	3634	3151	3151
q11	589	425	391	391
q12	656	664	511	511
q13	3374	3594	3083	3083
q14	300	301	271	271
q15	q16	648	689	617	617
q17	7822	6981	6909	6909
q18	13080	12241	12964	12241
q19	926	829	837	829
q20	2159	2173	1937	1937
q21	5715	4645	4684	4645
q22	519	459	406	406
Total cold run time: 71105 ms
Total hot run time: 64648 ms

@hello-stephen

Copy link
Copy Markdown
Contributor
TPC-DS: Total hot run time: 154070 ms
machine: 'aliyun_ecs.c7a.8xlarge_32C64G'
scripts: https://github.com/apache/doris/tree/master/tools/tpcds-tools
TPC-DS sf100 test result on commit 2e57ef18210c32cea99f6843fe317399f9e47b82, data reload: false

query5	4301	567	440	440
query6	421	216	185	185
query7	4222	435	230	230
query8	318	163	154	154
query9	7770	3180	3193	3180
query10	485	302	259	259
query11	4717	3845	3563	3563
query12	153	89	85	85
query13	501	462	331	331
query14	4306	3920	3639	3639
query14_1	3515	3437	3412	3412
query15	195	203	176	176
query16	607	436	432	432
query17	625	650	544	544
query18	406	424	312	312
query19	182	172	141	141
query20	85	86	86	86
query21	152	134	106	106
query22	12885	12996	12812	12812
query23	13578	12951	12539	12539
query23_1	12517	12598	12507	12507
query24	6912	835	474	474
query24_1	491	494	495	494
query25	539	433	373	373
query26	1243	260	139	139
query27	2742	434	281	281
query28	4572	2062	2024	2024
query29	1533	600	461	461
query30	298	219	173	173
query31	1010	943	818	818
query32	138	105	87	87
query33	510	311	254	254
query34	989	864	509	509
query35	747	742	634	634
query36	822	824	737	737
query37	125	95	81	81
query38	1833	1730	1644	1644
query39	793	787	747	747
query39_1	729	734	755	734
query40	216	118	101	101
query41	73	70	71	70
query42	94	86	81	81
query43	352	359	313	313
query44	1321	707	711	707
query45	181	174	174	174
query46	834	961	576	576
query47	2887	2580	2869	2580
query48	299	342	223	223
query49	561	418	294	294
query50	672	276	212	212
query51	10650	10585	10611	10585
query52	79	83	70	70
query53	187	210	150	150
query54	237	201	178	178
query55	74	69	66	66
query56	249	211	216	211
query57	1659	1619	1569	1569
query58	280	262	256	256
query59	2165	2242	2025	2025
query60	289	246	227	227
query61	151	148	152	148
query62	385	333	291	291
query63	187	159	165	159
query64	2754	983	787	787
query65	3356	3305	3295	3295
query66	1751	418	307	307
query67	22618	22579	22486	22486
query68	2663	1013	617	617
query69	384	283	250	250
query70	855	794	787	787
query71	297	232	224	224
query72	2700	2564	2184	2184
query73	508	530	298	298
query74	4634	4527	4354	4354
query75	2343	2284	1899	1899
query76	2259	1017	592	592
query77	346	363	274	274
query78	9899	9757	9244	9244
query79	1299	856	529	529
query80	542	426	347	347
query81	532	322	268	268
query82	474	133	113	113
query83	299	204	186	186
query84	316	117	104	104
query85	864	434	381	381
query86	378	237	234	234
query87	1965	1962	1831	1831
query88	3806	2801	2803	2801
query89	445	284	241	241
query90	1724	168	167	167
query91	162	149	128	128
query92	107	106	88	88
query93	1110	996	582	582
query94	464	300	272	272
query95	560	330	360	330
query96	692	479	244	244
query97	2448	2458	2310	2310
query98	160	155	155	155
query99	776	781	642	642
Total cold run time: 223847 ms
Total hot run time: 154070 ms

@hello-stephen

Copy link
Copy Markdown
Contributor
ClickBench: Total hot run time: 24.29 s
machine: 'aliyun_ecs.c7a.8xlarge_32C64G'
scripts: https://github.com/apache/doris/tree/master/tools/clickbench-tools
ClickBench test result on commit 2e57ef18210c32cea99f6843fe317399f9e47b82, data reload: false

query1	0.01	0.01	0.00
query2	0.10	0.05	0.04
query3	0.26	0.09	0.09
query4	1.61	0.09	0.10
query5	0.24	0.24	0.24
query6	1.24	0.69	0.68
query7	0.04	0.00	0.00
query8	0.06	0.04	0.04
query9	0.41	0.31	0.36
query10	0.58	0.57	0.60
query11	0.20	0.15	0.14
query12	0.19	0.15	0.15
query13	0.48	0.50	0.49
query14	1.05	1.01	1.01
query15	0.62	0.60	0.60
query16	0.36	0.31	0.34
query17	1.17	1.16	1.11
query18	0.21	0.19	0.19
query19	2.14	1.98	1.95
query20	0.02	0.01	0.01
query21	15.42	0.19	0.13
query22	4.86	0.05	0.06
query23	16.30	0.33	0.12
query24	3.03	0.53	0.46
query25	0.09	0.08	0.04
query26	1.54	0.23	0.16
query27	0.04	0.05	0.03
query28	3.23	0.75	0.38
query29	12.46	4.17	3.23
query30	0.28	0.12	0.10
query31	2.78	0.57	0.32
query32	3.22	0.59	0.51
query33	3.34	3.27	3.21
query34	15.46	3.99	3.40
query35	3.36	3.34	3.37
query36	0.56	0.45	0.44
query37	0.08	0.07	0.06
query38	0.05	0.04	0.03
query39	0.04	0.03	0.03
query40	0.18	0.15	0.14
query41	0.09	0.03	0.03
query42	0.03	0.03	0.04
query43	0.04	0.03	0.03
Total cold run time: 97.47 s
Total hot run time: 24.29 s

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants