Skip to content

Core, Open-API, Spark, Flink: Use String.replace for literal replacements - #17484

Open
uros-b wants to merge 3 commits into
apache:mainfrom
uros-b:core-expressionparser-use-string-replace
Open

Core, Open-API, Spark, Flink: Use String.replace for literal replacements#17484
uros-b wants to merge 3 commits into
apache:mainfrom
uros-b:core-expressionparser-use-string-replace

Conversation

@uros-b

@uros-b uros-b commented Aug 2, 2026

Copy link
Copy Markdown
Member

Several places used String.replaceAll with literal (non-regex) patterns, which compiles a regex Pattern on every call. Switch these to the char/CharSequence String.replace overloads, which avoid the per-call compilation. Behavior-identical.

Covered call sites: ExpressionParser and ReportMetricsRequestParser (core), RCKUtils (open-api), CreateChangelogViewProcedure and TestRewriteDataFilesProcedure (Spark 3.5, 4.0, 4.1), and MetricsReporterFactoryForTests (Flink 1.20, 2.0, 2.1). This covers every remaining literal replaceAll call; the ones left in the repo are genuine regexes.

operationType and fromType replaced single literal characters using String.replaceAll, which compiles a regex Pattern on every call. The patterns contain no regex metacharacters, so this switches to the char-based String.replace, avoiding the per-call compilation. Behavior-identical.
@github-actions github-actions Bot added the core label Aug 2, 2026

@uros-b uros-b left a comment

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

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

Waiting for CI, cc @szehon-ho for review

@ebyhr ebyhr left a comment

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.

Could you confirm other replaceAll usages? e.g. ReportMetricsRequestParser, RCKUtils

@uros-b

uros-b commented Aug 3, 2026

Copy link
Copy Markdown
Member Author

Good catch, thank you @ebyhr!

@uros-b uros-b changed the title Core: Use String.replace for literal replacements in ExpressionParser Core, Open-API: Use String.replace for literal replacements Aug 3, 2026
@uros-b
uros-b requested a review from ebyhr August 3, 2026 09:22
@ebyhr

ebyhr commented Aug 4, 2026

Copy link
Copy Markdown
Member

We can update more:

TableIdentifier.of("default", QUOTED_SPECIAL_CHARS_TABLE_NAME.replaceAll("`", ""));

TableIdentifier.of("default", QUOTED_SPECIAL_CHARS_TABLE_NAME.replaceAll("`", ""));

TableIdentifier.of("default", QUOTED_SPECIAL_CHARS_TABLE_NAME.replaceAll("`", ""));

@uros-b uros-b changed the title Core, Open-API: Use String.replace for literal replacements Core, Open-API, Spark, Flink: Use String.replace for literal replacements Aug 4, 2026
@uros-b

uros-b commented Aug 7, 2026

Copy link
Copy Markdown
Member Author

Thank you @ebyhr! Made additional changes, PTAL. Also cc @szehon-ho

@uros-b

uros-b commented Aug 19, 2026

Copy link
Copy Markdown
Member Author

cc @huaxingao

@nssalian
nssalian requested review from RussellSpitzer, ebyhr and huaxingao and removed request for ebyhr August 19, 2026 20:25
@RussellSpitzer

Copy link
Copy Markdown
Member

So this is one of those PR's that I know some folks are not a big fan of. We are touching a lot of files (lot of churn) and while there is a theoretical performance benefit here, in practice it is probably a noop.

Personally I don't mind this but it is worth questioning "Why" is this necessary. Is there a practical reason for this?

@nssalian

Copy link
Copy Markdown
Collaborator

It was initially scoped to the ExpressionParser which was ok to go in but expanded. +1 on Russell's comment.

@uros-b

uros-b commented Aug 19, 2026

Copy link
Copy Markdown
Member Author

Yeah I agree with you folks, shall we scope back to original scope (ExpressionParser) and proceed with narrow change?

@uros-b

uros-b commented Aug 19, 2026

Copy link
Copy Markdown
Member Author

I'm fine either way, I think it's worth fixing but also want to strike the correct scope (not too narrow, not too wide). Let me know what you think!

@szehon-ho

Copy link
Copy Markdown
Member

Hm, replace is better for perf than replaceAll. It's not called enough to make huge difference, but I dont see why it's a bad improvement. Moreover, I think replaceAll doesnt make sense here, as replaceAll interprets regex's (which is not what is meant in these call sites.)

Maybe we can change the core files? I dont think those particular files are touched that much? The test changes probably wont matter that much to warrant church, and we can do it separately if needed

@RussellSpitzer

Copy link
Copy Markdown
Member

Hm, replace is better for perf than replaceAll. It's not called enough to make huge difference, but I dont see why it's a bad improvement. Moreover, I think replaceAll doesnt make sense here, as replaceAll interprets regex's (which is not what is meant in these call sites.)

Maybe we can change the core files? I dont think those particular files are touched that much? The test changes probably wont matter that much to warrant church, and we can do it separately if needed

So the general call here is how much do we want to encourage PR's that are essentially a Noop on the project (not actually more secure or faster.) I'd feel better if we didn't already have such a huge burden on reviewers but this is more of a philosophical question. We want to encourage contributors to make meaningful changes to the code base. Now in this case if we think this is important and these are the only cases where it happens, rather than a one off correction we should be locking this out of the codebase entirely.

<module name="RegexpSinglelineJava">
    <property name="id" value="LiteralStringReplaceAll"/>
    <property name="ignoreComments" value="true"/>
    <property name="format" value="\.replaceAll\(\s*&quot;([^&quot;\\\\.*+?^$()\[\]{}|]*)&quot;"/>
    <property name="message" value="Use String.replace for literal replacements; replaceAll compiles a regex on every call."/>
</module>

Again this is only a rule I would add if we already have a tiny surface area of places to fix since again there really isn't a benefit to doing this but if we can lock out all future mistakes that feels valid to me.

In case folks aren't aware, what i'm trying to avoid is a situation like #16881 where we did a rather large PR which was a also essentially a Noop but it ended up being reverted for the same rational I noted above. That's also being replicated again in #17534 .

@szehon-ho

szehon-ho commented Aug 20, 2026

Copy link
Copy Markdown
Member

So iiuc, the concern from switch discussion is touching too many files to force pr rebase.

It seems nice to change replaceAll and lock it via the rule as you suggest. I had actually thought about it yesterday, but if we want to only allow replaceAll for regex literals, your rule is too strong. Or we could just ban replaceAll for all literal and just force explicit Pattern.compile(...).replaceAll, (which sounds ok to me too, more explicit)

Yea agree its not the most important thing in the world, but the surface area does not seem as big here as the switch pr (84 files there..), but I may have missed additional context from missing that conversation

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.

5 participants