Skip to content

[improvement](parser) Add case-insensitive stream fast paths - #67452

Merged
morrySnow merged 4 commits into
apache:masterfrom
morrySnow:codex/antlr4-case-folding-fast-path
Sep 10, 2026
Merged

morrySnow merged 4 commits into
apache:masterfrom
morrySnow:codex/antlr4-case-folding-fast-path

Conversation

@morrySnow

Copy link
Copy Markdown
Contributor

What problem does this PR solve?

Issue Number: None

Related PR: None

Problem Summary:

Every SQL lexer currently copies its input through CharStreams.fromString() and calls Character.toUpperCase() for every case-insensitive lookahead. This is visible in lexer allocation and identifier-heavy parsing time.

Add two strictly compatible fast paths:

  • Fold ASCII a-z with arithmetic and retain Character.toUpperCase() for every other code point.
  • Read strings without UTF-16 surrogates directly, avoiding ANTLR's copied code-point buffer. Inputs containing any surrogate retain the original ANTLR stream so code-point indices, getText, navigation, and errors remain unchanged.

The public arbitrary-CharStream constructor is preserved. All production String entry points now use the factory. ANTLR's native caseInsensitive option was evaluated but rejected because it changed existing Unicode behavior such as the handling of ſelect.

Benchmark

Environment and method:

  • Baseline: cf33a08bcd5, artifact SHA-256 51700bef953361218912abc6aec348cecdec3fe61031eba10cc1ebc534bc9183
  • Candidate: 7aa7b5e6487, artifact SHA-256 6e8edf78befc0dc349be398aae454c172b5cb968ee98d88a0e056d4c21a9c5d0
  • macOS 15.0.1 arm64, OpenJDK 17.0.20.1, 1 GiB heap, JMH 1.37
  • 2 forks, 4 x 300 ms warmup, 7 x 400 ms measurement, -prof gc
  • Interleaved baseline/candidate runs; one candidate run affected by unrelated host load was discarded and repeated. The table averages two valid runs per artifact. Individual JMH scores use 99.9% confidence intervals.
java -Xms1g -Xmx1g -jar <benchmark.jar> \
  'CaseInsensitiveStreamBenchmark.(createLexer|foldPrebuiltCharacters|parseStatement|tokenize)' \
  -p workload=shortQuery,lowercaseIdentifiers,stringAndComment,unicode \
  -f 2 -wi 4 -i 7 -w 300ms -r 400ms -prof gc -rf json
Benchmark Baseline us/op (B1 / B2) Candidate us/op (C1 / C3) Mean change Baseline -> candidate B/op
Tokenize 64 lowercase identifiers 10.826±0.078 / 11.408±0.125 8.708±0.071 / 8.704±0.096 -21.7% 14,799 -> 10,464 (-29.3%)
Parse 64 lowercase identifiers 54.775±7.360 / 56.365±4.224 49.860±0.191 / 52.340±1.160 -8.0% 99,395 -> 95,017 (-4.4%)
Tokenize select 1 0.451±0.004 / 0.485±0.011 0.348±0.069 / 0.324±0.010 -28.2% 1,008 -> 816 (-19.0%)
Parse strings/comments with Unicode 27.047±5.371 / 28.435±2.932 24.744±0.401 / 24.743±0.425 -10.8% 24,167 -> 23,784 (-1.6%)
Tokenize supplementary Unicode control 32.034±1.398 / 33.910±0.752 32.303±1.108 / 32.020±0.738 -2.5% 24,630 -> 24,594 (-0.1%)

The isolated createLexer measurement for the surrogate-containing string/comment workload regresses by 5.9% because the compatibility guard scans for surrogates before falling back. The corresponding complete tokenize and parse paths improve by 3.6% and 10.8%; no end-to-end control workload regressed. Prebuilt lowercase character folding improves by 10.6%.

The performance gains come from eliminating the copied input buffer for BMP-only SQL, avoiding its allocation, and replacing the common lowercase ASCII Character.toUpperCase() call with an arithmetic branch.

Correctness corpus:

  • 4,610 tracked SQL files; baseline and candidate parse signatures match in legacy and ANSI modes. SHA-256: 69c811d0d80c52b40d8cd854e925ff4930aa6601c7d1e66541f2eb29f35db50a.
  • 9,220 lexer cases (4,610 SQL files x both noBackslashEscapes modes); complete token tuple and lexer-error signatures match. SHA-256: 66780d4e1d9224ea27c8f715ab33edd247faed1571030ba9e90a8ef3f6212180.

Release note

None

Check List (For Author)

  • Test
    • Regression test
    • Unit Test
    • Manual test (add detailed scripts or steps below)
    • No need to test or manual test. Explain why:
      • This is a refactor/code format and no logic has been changed.
      • Previous test can cover this change.
      • No code files have been changed.
      • Other reason

Tested with:

  • mvn -Pbenchmark -pl fe-sql-parser,fe-sql-parser-benchmark -Dmaven.build.cache.enabled=false clean package: 170 tests passed; Checkstyle passed.

  • ./run-fe-ut.sh --run org.apache.doris.httpv2.websql.SingleStatementValidatorTest,org.apache.doris.nereids.parser.NereidsParserTest,org.apache.doris.nereids.parser.NereidsParserDigestTest: 91 tests passed.

  • Token and parse corpus comparisons described above: no differences.

  • FE-only ./build.sh --fe with a fresh output directory: passed and produced Successfully build Doris.

  • Full ./run-fe-ut.sh was attempted, but the host's Surefire JVM stopped during the pre-existing fe-connector-trino TrinoPredicateConverterTest dynamic-attach path before reaching fe-core; it was terminated after 90 minutes with no P4-related failure.

  • Behavior changed:

    • No.
    • Yes.
  • Does this need documentation?

    • No.
    • Yes.

Check List (For Reviewer who merge this PR)

  • Confirm the release note
  • Confirm test cases
  • Confirm document
  • Add branch pick label

@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?

@morrySnow morrySnow changed the title [improvement](fe) Add case-insensitive stream fast paths [improvement](parser) Add case-insensitive stream fast paths Sep 2, 2026
@morrySnow

Copy link
Copy Markdown
Contributor Author

run buildall

@hello-stephen

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

------ Round 1 ----------------------------------
============================================
q1	17566	3065	3050	3050
q2	2117	264	232	232
q3	10221	926	512	512
q4	4672	252	203	203
q5	7680	569	390	390
q6	137	119	93	93
q7	536	520	384	384
q8	9245	884	928	884
q9	3410	2397	2384	2384
q10	6557	849	732	732
q11	393	203	185	185
q12	608	263	203	203
q13	18133	1508	1147	1147
q14	160	151	142	142
q15	q16	435	397	368	368
q17	1399	875	808	808
q18	3145	2222	2204	2204
q19	1303	865	790	790
q20	406	294	198	198
q21	5622	1805	1827	1805
q22	338	274	228	228
Total cold run time: 94083 ms
Total hot run time: 16942 ms

----- Round 2, with runtime_filter_mode=off -----
============================================
q1	3444	3387	3331	3331
q2	506	396	372	372
q3	2182	2245	2103	2103
q4	1193	1155	897	897
q5	2163	2115	2082	2082
q6	168	121	86	86
q7	1012	903	834	834
q8	1598	1404	1408	1404
q9	3136	3099	3112	3099
q10	1897	1777	1629	1629
q11	353	270	252	252
q12	453	426	349	349
q13	1486	1534	1147	1147
q14	171	173	167	167
q15	q16	389	395	366	366
q17	3495	3368	3226	3226
q18	4779	4392	4699	4392
q19	875	883	889	883
q20	1004	958	828	828
q21	3851	3085	3270	3085
q22	411	350	321	321
Total cold run time: 34566 ms
Total hot run time: 30853 ms

@hello-stephen

Copy link
Copy Markdown
Contributor
TPC-DS: Total hot run time: 81938 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 7aa7b5e648734ed846679e93886c2d3fb2187002, data reload: false

query5	4248	411	346	346
query6	399	137	133	133
query7	4925	405	232	232
query8	287	122	116	116
query9	8702	2907	2897	2897
query10	396	218	173	173
query11	5403	1045	921	921
query12	124	73	70	70
query13	1215	446	353	353
query14	6069	2227	2114	2114
query14_1	2011	1969	1998	1969
query15	177	123	110	110
query16	927	373	341	341
query17	799	470	378	378
query18	2345	328	239	239
query19	171	142	115	115
query20	74	71	71	71
query21	203	107	91	91
query22	5416	5575	5198	5198
query23	6730	6158	5969	5969
query23_1	5959	6303	5920	5920
query24	7278	1115	745	745
query24_1	792	785	813	785
query25	447	307	263	263
query26	1242	249	128	128
query27	2778	465	245	245
query28	4649	1496	1489	1489
query29	929	421	346	346
query30	247	159	129	129
query31	821	399	325	325
query32	143	74	73	73
query33	455	214	165	165
query34	1005	810	490	490
query35	391	411	335	335
query36	583	567	543	543
query37	125	82	70	70
query38	996	839	822	822
query39	516	476	488	476
query39_1	477	432	460	432
query40	203	99	79	79
query41	56	55	52	52
query42	75	72	72	72
query43	233	238	213	213
query44	1027	564	559	559
query45	108	104	102	102
query46	751	851	536	536
query47	749	765	696	696
query48	293	311	222	222
query49	521	238	185	185
query50	770	272	201	201
query51	8226	8105	8126	8105
query52	68	67	57	57
query53	194	198	153	153
query54	240	172	159	159
query55	80	62	60	60
query56	213	210	189	189
query57	674	656	677	656
query58	221	162	163	162
query59	1193	1224	1067	1067
query60	234	173	169	169
query61	109	121	119	119
query62	348	200	186	186
query63	173	148	143	143
query64	2809	678	625	625
query65	1657	1599	1614	1599
query66	1839	275	205	205
query67	10536	9744	9696	9696
query68	3001	1255	721	721
query69	358	224	201	201
query70	662	609	613	609
query71	258	174	169	169
query72	2430	1743	1580	1580
query73	613	638	344	344
query74	2007	1238	1126	1126
query75	1181	1098	949	949
query76	2377	740	569	569
query77	260	252	223	223
query78	3768	3513	3147	3147
query79	2847	768	581	581
query80	1593	351	282	282
query81	494	155	132	132
query82	987	135	101	101
query83	302	212	194	194
query84	305	112	96	96
query85	828	357	291	291
query86	414	181	175	175
query87	1023	981	876	876
query88	2774	2117	2102	2102
query89	307	193	174	174
query90	1913	125	128	125
query91	136	132	103	103
query92	82	71	70	70
query93	1608	1036	730	730
query94	664	274	209	209
query95	518	256	306	256
query96	756	620	285	285
query97	1035	1054	1024	1024
query98	164	133	133	133
query99	437	359	306	306
Total cold run time: 179585 ms
Total hot run time: 81938 ms

@hello-stephen

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

query1	0.00	0.00	0.00
query2	0.07	0.03	0.03
query3	0.24	0.11	0.11
query4	1.60	0.09	0.09
query5	0.17	0.15	0.16
query6	1.25	0.67	0.69
query7	0.03	0.00	0.01
query8	0.05	0.03	0.06
query9	0.29	0.21	0.21
query10	0.37	0.34	0.34
query11	0.17	0.11	0.12
query12	0.14	0.11	0.12
query13	0.31	0.31	0.30
query14	0.45	0.45	0.44
query15	0.37	0.35	0.35
query16	0.22	0.23	0.24
query17	0.69	0.73	0.71
query18	0.18	0.18	0.16
query19	1.18	1.08	1.05
query20	0.01	0.01	0.01
query21	15.48	0.15	0.11
query22	5.07	0.04	0.04
query23	16.20	0.25	0.10
query24	3.03	0.31	0.26
query25	0.13	0.05	0.03
query26	0.77	0.17	0.13
query27	0.04	0.02	0.03
query28	3.74	0.56	0.27
query29	12.42	3.19	2.56
query30	0.27	0.11	0.12
query31	2.76	0.38	0.17
query32	3.51	0.33	0.22
query33	1.53	1.38	1.42
query34	15.39	2.25	1.78
query35	1.78	1.74	1.71
query36	0.46	0.29	0.30
query37	0.07	0.04	0.04
query38	0.04	0.03	0.03
query39	0.03	0.03	0.02
query40	0.12	0.07	0.08
query41	0.09	0.02	0.03
query42	0.03	0.03	0.03
query43	0.03	0.03	0.03
Total cold run time: 90.78 s
Total hot run time: 14.52 s

@hello-stephen

Copy link
Copy Markdown
Contributor

FE Regression Coverage Report

Increment line coverage 6.82% (3/44) 🎉
Increment coverage report
Complete coverage report

### What problem does this PR solve?

Issue Number: None

Related PR: None

Problem Summary: Add a reference-based character stream and lexer differential test plus JMH coverage for stream creation, case-folding lookahead, tokenization, and end-to-end parsing. This freezes a shared harness before evaluating P4 implementations.

### Release note

None

### Check List (For Author)

- Test: Unit Test
    - mvn -Pbenchmark -pl fe-sql-parser,fe-sql-parser-benchmark -Dmaven.build.cache.enabled=false package (170 tests passed)
- Behavior changed: No
- Does this need documentation: No
### What problem does this PR solve?

Issue Number: None

Related PR: None

Problem Summary: CaseInsensitiveStream called Character.toUpperCase for every lexer lookahead. Fold ASCII lowercase characters with arithmetic while preserving the Character.toUpperCase fallback for all other code points. Exploratory JMH reduced prebuilt lowercase folding from 2.041 to 1.570 us/op and uppercase folding from 2.044 to 1.598 us/op.

### Release note

None

### Check List (For Author)

- Test: Unit Test
    - mvn -pl fe-sql-parser -Dmaven.build.cache.enabled=false -Dtest=CaseInsensitiveStreamTest test
- Behavior changed: No
- Does this need documentation: No
### What problem does this PR solve?

Issue Number: None

Related PR: None

Problem Summary: ANTLR copies every Java String into a code-point buffer before lexing. Read strings without surrogate code units directly while retaining the original ANTLR stream whenever code-point and UTF-16 indices differ. A 3-fork exploratory JMH run reduced identifier-heavy tokenization by 27.0%-28.2% and allocation by about 29%; short-query tokenization improved by 22.1%.

### Release note

None

### Check List (For Author)

- Test: Unit Test
    - mvn -pl fe-sql-parser -Dmaven.build.cache.enabled=false -Dtest=CaseInsensitiveStreamTest test
    - ./run-fe-ut.sh --run org.apache.doris.httpv2.websql.SingleStatementValidatorTest,org.apache.doris.nereids.parser.NereidsParserTest,org.apache.doris.nereids.parser.NereidsParserDigestTest (91 tests passed)
- Behavior changed: No
- Does this need documentation: No
@morrySnow
morrySnow force-pushed the codex/antlr4-case-folding-fast-path branch from 7aa7b5e to 9253bbb Compare September 8, 2026 07:26
@morrySnow

Copy link
Copy Markdown
Contributor Author

run buildall

@morrySnow

Copy link
Copy Markdown
Contributor Author

run buildall

@morrySnow
morrySnow marked this pull request as ready for review September 9, 2026 07:52
@hello-stephen

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

------ Round 1 ----------------------------------
============================================
q1	17564	2998	2983	2983
q2	2145	261	231	231
q3	10201	860	517	517
q4	4670	249	202	202
q5	7673	573	387	387
q6	135	116	93	93
q7	526	573	386	386
q8	9245	926	946	926
q9	3458	2428	2377	2377
q10	6531	857	697	697
q11	404	195	182	182
q12	612	258	193	193
q13	18143	1559	1150	1150
q14	159	155	138	138
q15	q16	437	399	369	369
q17	1401	909	810	810
q18	3120	2274	2224	2224
q19	1273	915	809	809
q20	379	283	200	200
q21	5608	1757	1835	1757
q22	341	276	237	237
Total cold run time: 94025 ms
Total hot run time: 16868 ms

----- Round 2, with runtime_filter_mode=off -----
============================================
q1	3353	3283	3256	3256
q2	507	384	382	382
q3	2205	2259	2193	2193
q4	1193	1166	895	895
q5	2195	2147	2102	2102
q6	165	119	87	87
q7	1027	952	884	884
q8	1591	1401	1386	1386
q9	3100	3068	3057	3057
q10	1880	1791	1649	1649
q11	350	266	247	247
q12	444	426	337	337
q13	1477	1550	1145	1145
q14	166	168	161	161
q15	q16	388	398	359	359
q17	3566	3260	3152	3152
q18	4803	4420	4748	4420
q19	861	832	932	832
q20	1025	959	844	844
q21	3856	3159	3269	3159
q22	403	354	325	325
Total cold run time: 34555 ms
Total hot run time: 30872 ms

@hello-stephen

Copy link
Copy Markdown
Contributor
TPC-DS: Total hot run time: 82319 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 33994438a09cf5f273fb5aa123511b62a1092b83, data reload: false

query5	4252	411	353	353
query6	377	140	122	122
query7	4948	418	223	223
query8	293	132	122	122
query9	8774	2894	2929	2894
query10	395	230	182	182
query11	5371	1040	907	907
query12	135	71	73	71
query13	1192	450	330	330
query14	6230	2217	2122	2122
query14_1	1999	1995	1993	1993
query15	179	119	119	119
query16	929	367	388	367
query17	807	465	368	368
query18	2340	344	244	244
query19	163	147	114	114
query20	99	68	71	68
query21	197	99	84	84
query22	5509	5390	5385	5385
query23	6911	6312	6021	6021
query23_1	5982	5951	6106	5951
query24	7252	1085	770	770
query24_1	793	777	800	777
query25	405	281	227	227
query26	1224	221	130	130
query27	2797	421	254	254
query28	4673	1500	1496	1496
query29	916	452	354	354
query30	263	154	131	131
query31	824	401	332	332
query32	127	88	74	74
query33	459	218	179	179
query34	983	833	498	498
query35	410	409	342	342
query36	579	582	522	522
query37	120	82	73	73
query38	1002	848	817	817
query39	487	494	474	474
query39_1	444	475	467	467
query40	203	89	74	74
query41	55	53	53	53
query42	75	74	72	72
query43	239	240	213	213
query44	1001	541	538	538
query45	109	106	102	102
query46	800	849	532	532
query47	763	812	727	727
query48	323	316	229	229
query49	540	253	201	201
query50	751	276	194	194
query51	8085	7948	7938	7938
query52	71	72	68	68
query53	194	202	148	148
query54	209	171	155	155
query55	80	62	55	55
query56	197	229	179	179
query57	700	657	606	606
query58	212	167	169	167
query59	1204	1243	1102	1102
query60	246	172	194	172
query61	139	136	112	112
query62	344	201	176	176
query63	174	146	136	136
query64	2741	782	747	747
query65	1674	1636	1684	1636
query66	1827	266	230	230
query67	9994	9751	10099	9751
query68	3016	1250	765	765
query69	363	229	200	200
query70	684	601	636	601
query71	261	188	177	177
query72	2412	1661	1511	1511
query73	668	596	340	340
query74	2013	1228	1142	1142
query75	1201	1107	969	969
query76	2390	745	525	525
query77	258	261	215	215
query78	3906	3686	3210	3210
query79	2739	818	586	586
query80	1617	370	262	262
query81	516	158	132	132
query82	621	128	97	97
query83	291	211	201	201
query84	300	115	84	84
query85	800	354	284	284
query86	468	170	165	165
query87	1035	964	901	901
query88	3000	2138	2136	2136
query89	283	200	179	179
query90	2079	136	138	136
query91	131	121	102	102
query92	82	73	81	73
query93	1871	1058	674	674
query94	627	260	211	211
query95	508	249	234	234
query96	758	537	278	278
query97	1102	1034	1031	1031
query98	169	137	152	137
query99	413	344	308	308
Total cold run time: 179775 ms
Total hot run time: 82319 ms

@hello-stephen

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

query1	0.01	0.01	0.01
query2	0.08	0.04	0.04
query3	0.25	0.11	0.10
query4	1.60	0.13	0.10
query5	0.18	0.15	0.16
query6	1.23	0.70	0.71
query7	0.04	0.01	0.00
query8	0.04	0.02	0.02
query9	0.27	0.21	0.21
query10	0.34	0.34	0.35
query11	0.17	0.12	0.12
query12	0.15	0.12	0.11
query13	0.29	0.30	0.29
query14	0.44	0.45	0.46
query15	0.36	0.35	0.35
query16	0.22	0.23	0.22
query17	0.75	0.67	0.72
query18	0.18	0.16	0.16
query19	1.21	1.09	1.21
query20	0.02	0.01	0.01
query21	15.44	0.15	0.12
query22	5.09	0.04	0.04
query23	16.23	0.25	0.10
query24	3.02	0.32	0.24
query25	0.11	0.04	0.03
query26	0.82	0.16	0.13
query27	0.04	0.03	0.03
query28	3.68	0.52	0.27
query29	12.42	3.16	2.56
query30	0.26	0.12	0.13
query31	2.76	0.36	0.18
query32	3.53	0.32	0.24
query33	1.50	1.45	1.50
query34	15.39	2.20	1.78
query35	1.76	1.77	1.75
query36	0.46	0.29	0.28
query37	0.06	0.04	0.04
query38	0.04	0.03	0.03
query39	0.03	0.03	0.02
query40	0.12	0.07	0.07
query41	0.07	0.02	0.03
query42	0.03	0.02	0.02
query43	0.04	0.03	0.02
Total cold run time: 90.73 s
Total hot run time: 14.68 s

@hello-stephen

Copy link
Copy Markdown
Contributor

FE Regression Coverage Report

Increment line coverage 1.69% (3/178) 🎉
Increment coverage report
Complete coverage report

@morrySnow
morrySnow merged commit a6a7e52 into apache:master Sep 10, 2026
40 of 41 checks passed
@morrySnow
morrySnow deleted the codex/antlr4-case-folding-fast-path branch September 10, 2026 04:08
@github-actions

Copy link
Copy Markdown
Contributor

PR approved by anyone and no changes requested.

@github-actions github-actions Bot added the approved Indicates a PR has been approved by one committer. label Sep 10, 2026
@github-actions

Copy link
Copy Markdown
Contributor

PR approved by at least one committer and no changes requested.

morningman added a commit to morningman/doris that referenced this pull request Nov 13, 2026
…characters

### Background

Several FE features rewrite the text of a statement by cutting the original SQL string at token positions from the parser. Audit masking replaces credential properties and passwords with `*XXX` before the statement reaches `fe.audit.log`, `__internal_schema.audit_log` and the FE log; CREATE / ALTER VIEW and CREATE MATERIALIZED VIEW expand `*` and qualify names in the definition they store; the parser reads `/*+ ... */` hints from comment tokens. The lexer counts its input by Unicode code point, while a Java `String` holds a supplementary character, such as an emoji, as two `char`s.

### What problem does this PR solve?

Issue Number: None
Related PR: apache#47595 (introduced the bug in audit masking), apache#32743 (introduced the bug in view rewriting), apache#70338 (its review found the masking leak)
PR Type: Bugfix

Problem Summary:

After each supplementary character in a statement, these token positions point one `char` too early, so the text is cut at the wrong place. A credential after or inside such characters stays partly or fully in clear text in the audit log, CREATE VIEW fails with a syntax error, CREATE MATERIALIZED VIEW stores a broken query, and a hint after such a character is silently ignored. This PR converts the positions to `String` offsets, or reads the text through the token, at the five places that cut the original SQL. Tests are 89% of the diff.

### Details

- **Change map:** production code +33 -33 in 3 files; tests +240 in 5 files (FE unit tests and one regression suite); generated `.out` +16. (1) `BaseViewInfo.rewriteSql`, shared by audit masking, views and MTMV, converts its positions; the sync-MV builder drops its private copy of it, and `rewriteProjectsToUserDefineAlias` replaces the select list through it. (2) `NereidsParser.getHintMap` reads hints from the token text. (3) `NereidsParser.tryParseExplainPlan` converts the start of the EXPLAIN PLAN body.
- **Frequency:** Always, when a character above U+FFFF (an emoji, CJK Extension B) comes before or inside the rewritten part, for example a catalog COMMENT of emoji in front of `'s3.secret_key' = 'secret'`.
- **Root cause:** `CaseInsensitiveStream.fromString` keeps ANTLR's code-point stream for any input with a surrogate (before apache#67452 every input used `CharStreams.fromString`), so `Token.getStartIndex()` and `getStopIndex()` count code points. The five places in the Change map used them as `String` indexes.
- **Why existing tests missed it:** Unit tests: the masking (`EncryptSQLTest`, `RepositoryAuditEncryptionTest`), view and MTMV (`CreateViewTest`, `CreateMTMVCommandTest`) and hint tests (`LeanTokenModeTest`) use only BMP text, where both kinds of position coincide; `CaseInsensitiveStreamTest` uses an emoji but checks only the stream's own positions. Regression: `audit/test_external_catalog_credential_masking` and the `ddl_p0/create_view_nereids` suites have no supplementary characters, and the masking suite only checks that the whole credential is absent, which a leaked tail passes.
- **Fix:** Convert where the text is cut. `rewriteSql` converts each position with `String.offsetByCodePoints`, walking the sorted positions once so the cost stays linear; `rewriteProjectsToUserDefineAlias` reads expressions through the token's input stream; `getHintMap` reads the hint from the token text, as `removeCommentAndTrimBlank` does. Changing the stream to report `String` positions was rejected: apache#67452 made code-point positions part of `CaseInsensitiveStream`'s contract, and `CaseInsensitiveStreamTest` pins them.
- **Other paths and branches:** Fixed here: the 19 masking visitors of `LogicalPlanBuilderForEncryption` with every caller of `geneEncryptionSQL` (audit log, FE statement log, `_query_plan`) and the streaming insert job; the view, MTMV and sync-MV rewrites. Not affected: the other uses of token positions read text through the token's own stream (such as `getOriginSql`) or only compare positions with each other. branch-3.1, branch-4.0, branch-4.1 and branch-4.2 have the same code. Introduced by apache#32743 (views), apache#47595 (masking), apache#33699 (sync MV) and apache#33262 (EXPLAIN PLAN); hints since at least apache#39590.

### Testing

- New unit tests fail on master `f1bc8b3690c` and pass with the fix, run locally: `EncryptSQLTest#testEncryptionAfterSupplementaryCharacters`, through `MysqlConnectProcessor` to the audit event (emoji inside a credential, in a catalog COMMENT, in the first of two property clauses, in a user name before `PASSWORD(...)`; master left `secret`, `abc`, `123` and `123456` in clear text); `CreateViewTest#testCreateViewRewritesAfterSupplementaryCharacters` (master: `mismatched input '`e`'`); `CreateMTMVCommandTest#testCreateMTMVRewritesQuerySqlAfterSupplementaryCharacters` (master kept ``SELECT '😀😀' A`e` e`internal`...``); `SupplementaryCharacterOffsetTest` (master dropped the hint, cut the EXPLAIN PLAN body to `AN SELECT 100`, and rewrote the sync MV to `avg_stateg(k)))`).
- New regression suite `ddl_p0/create_view_nereids/test_create_view_supplementary_chars`: fails without the fix with the same syntax error and passes with it; the `.out` is generated by the script. Run locally on one FE and one BE, an older master FE build with the three changed classes swapped in.
- 27 existing FE UT classes around the change (467 tests) pass. Reverting each part of the fix alone, moving the end of a replaced span by one, or not advancing the position cursor each fails a new test; the off-by-one also fails existing ASCII cases.
- By hand on that cluster, without the fix: the secret stayed in `audit_log`, `/* 😀 */ select /*+ SET_VAR(query_timeout=1) */ sleep(3)` ran for 3 s, and an MTMV over `select '😀😀' as e, ...` was created but failed to refresh (`Nereids parse failed`). With the fix: `"s3.secret_key" = "*XXX")`, and the query times out after 1 s.

### Notes for Reviewers

- `getHintMap` loses its `sql` parameter, which nothing reads any more; its three callers are in `NereidsParser`.
- `rewriteProjectsToUserDefineAlias` replaces the select list through `rewriteSql` instead of `StringUtils.overlay`, so the conversion lives in one method.
- Stored definitions are not repaired: an MTMV created by an affected version from such a query keeps its broken definition and has to be recreated.

### Release note

Fix passwords and credentials left partly or fully unmasked in the audit log and the FE log when a supplementary character, such as an emoji, comes before or inside them. Also fix CREATE VIEW failing, CREATE MATERIALIZED VIEW storing a broken query, and hints such as SET_VAR being ignored after such a character; materialized views already created with a broken query must be recreated.

### Check List (For Author)

- Behavior changed: No
- Does this need documentation: No
- Pre-submission status: Ready

### AI Disclosure

- Tool: Claude Code (Claude Opus 5.5)
- Generated: the fix, the tests, and this description
- Human review: The author reviewed part of the changes.
- Prompts: After the review bot on apache#70338 reported the masking leak, the author confirmed it is an existing engine defect shared by all masking paths and asked for a separate engine PR that fixes it, followed by a reply on apache#70338 with the link.

Co-Authored-By: Claude Opus 5.5 <[email protected]>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

approved Indicates a PR has been approved by one committer. reviewed

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants