fix: apply regexp_instr subexpr to the N-th match, not the first - #24991
Open
MaxFreedomPollard wants to merge 1 commit into
Open
fix: apply regexp_instr subexpr to the N-th match, not the first#24991MaxFreedomPollard wants to merge 1 commit into
MaxFreedomPollard wants to merge 1 commit into
Conversation
regexp_instr(str, regexp, start, N, flags, subexpr) ignored N whenever subexpr was greater than zero, always reporting a position inside the first match. Asking for an occurrence that does not exist returned a position instead of 0. get_index in datafusion/functions/src/regex/regexpinstr.rs selected the match with Regex::captures, which only ever returns the first match, and dropped n on that branch. It now uses Regex::captures_iter(...).nth(n - 1), the counterpart of the find_iter(...).nth(n - 1) the subexpr == 0 branch already used, so both branches count occurrences the same way. Every existing test of subexpr used N = 1, which is how this survived. test_case_sensitive_regexp_instr_scalar_subexp gains N = 2 for each of the three capture groups, N = 3 where no such match exists, and a case combining start with subexpr. regexp/regexp_instr.slt gains the two SQL queries that changed.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Which issue does this PR close?
get_indexinregexpinstr.rsto work out howNandsubexprare meant to combine. Happy to file one if the changelog needs the link.Rationale for this change
regexp_instrignoresNwheneversubexpris greater than zero. It always reports a position inside the first match, whichever occurrence you asked for.The second one is the worse shape: a query that asks for an occurrence which does not exist gets a real position back, so nothing in the result says the match was missing.
PostgreSQL, which this function follows, defines
regexp_instras returning "the starting or ending position of the N'th match", andsubexpras "an integer indicating which subexpression is of interest: the result identifies the position of the substring matching that subexpression" of that match. DataFusion's own doc string promises the two things independently: "N: Optional The N-th occurrence of pattern to find" and "subexpr: Optional Specifies which capture group (subexpression) to return the position for".What changes are included in this PR?
get_indexindatafusion/functions/src/regex/regexpinstr.rspicked the match withRegex::captures, which only ever returns the first match, and droppednon that branch. It now usesRegex::captures_iter(...).nth(n - 1), the capture-group counterpart of thefind_iter(...).nth(n - 1)thesubexpr == 0branch already used, so both branches count occurrences the same way. The comment claiming subexpr took precedence over N goes with it.N = 1is unchanged, becausecaptures_iter(...).nth(0)andcaptures(...)are the same match. That is why no existing test or.sltexpectation moves.What is the testing strategy for this PR?
Every existing test of
subexpr, inregexpinstr.rsand inregexp/regexp_instr.sltalike, usedN = 1, which is how this survived.test_case_sensitive_regexp_instr_scalar_subexpgainsN = 2for each of the three capture groups,N = 3where no such match exists, and astart = 18row sostartandsubexprare exercised together. The harness already runs every row throughUtf8,LargeUtf8andUtf8View.regexp/regexp_instr.sltgains the two queries above.Reverting only
regexpinstr.rstomainand keeping the new tests, both fail:With the fix, on aarch64-apple-darwin, rustc 1.97.0:
Are there any user-facing changes?
Yes.
regexp_instr(str, regexp, start, N, flags, subexpr)withNgreater than 1 andsubexprgreater than 0 now returns a position in the N'th match, and 0 when there is no N'th match. No API change.One thing I did not touch:
expr_fn::regexp_instrindatafusion/functions/src/regex/mod.rstakes anendoptionargument and pushes it as the fifth positional argument. PostgreSQL has that parameter, but this UDF does not, so its fifth argument isflags. Any caller passingSome(endoption)builds a seven-argument call that no signature accepts. That looked like a separate change to me, and possibly an API break, so I left it alone.