]> granicus.if.org Git - postgresql/commitdiff
Fix incorrect search for "x?" style matches in creviterdissect().
authorTom Lane <tgl@sss.pgh.pa.us>
Wed, 24 Sep 2014 00:25:33 +0000 (20:25 -0400)
committerTom Lane <tgl@sss.pgh.pa.us>
Wed, 24 Sep 2014 00:26:21 +0000 (20:26 -0400)
When the number of allowed iterations is limited (either a "?" quantifier
or a bound expression), the last sub-match has to reach to the end of the
target string.  The previous coding here first tried the shortest possible
match (one character, usually) and then gave up and back-tracked if that
didn't work, typically leading to failure to match overall, as shown in
bug #11478 from Christoph Berg.  The minimum change to fix that would be to
not decrement k before "goto backtrack"; but that would be a pretty stupid
solution, because we'd laboriously try each possible sub-match length
before finally discovering that only ending at the end can work.  Instead,
force the sub-match endpoint limit up to the end for even the first
shortest() call if we cannot have any more sub-matches after this one.

Bug introduced in my rewrite that added the iterdissect logic, commit
173e29aa5deefd9e71c183583ba37805c8102a72.  The shortest-first search code
was too closely modeled on the longest-first code, which hasn't got this
issue since it tries a match reaching to the end to start with anyway.
Back-patch to all affected branches.

src/backend/regex/regexec.c
src/test/regress/expected/regex.out
src/test/regress/sql/regex.sql

index 7f41437cb58e6b8f15589c64a03f6c58eccabd69..5e78f8149c8783291b489a8db980d417e314f46d 100644 (file)
@@ -1190,6 +1190,10 @@ creviterdissect(struct vars * v,
                        (k >= min_matches || min_matches - k < end - limit))
                        limit++;
 
+               /* if this is the last allowed sub-match, it must reach to the end */
+               if (k >= max_matches)
+                       limit = end;
+
                /* try to find an endpoint for the k'th sub-match */
                endpts[k] = shortest(v, d, endpts[k - 1], limit, end,
                                                         (chr **) NULL, (int *) NULL);
index df39ef937dd4040a5b2d2cb0bbcea77bc534d30b..497ddcd4677be7ca819cb92e6cfecbdb388f3b89 100644 (file)
@@ -188,3 +188,11 @@ select regexp_matches('Programmer', '(\w)(.*?\1)', 'g');
  {m,m}
 (2 rows)
 
+-- Test for proper matching of non-greedy iteration (bug #11478)
+select regexp_matches('foo/bar/baz',
+                      '^([^/]+?)(?:/([^/]+?))(?:/([^/]+?))?$', '');
+ regexp_matches 
+----------------
+ {foo,bar,baz}
+(1 row)
+
index e5f690263b9c2002ff707ff7fd4337f2fc837fb0..ceb9d699ce96e17ac913a33d7384477d755d5ac1 100644 (file)
@@ -46,3 +46,7 @@ select 'a' ~ '((((((a+|)+|)+|)+|)+|)+|)';
 -- https://core.tcl.tk/tcl/tktview/6585b21ca8fa6f3678d442b97241fdd43dba2ec0
 select 'Programmer' ~ '(\w).*?\1' as t;
 select regexp_matches('Programmer', '(\w)(.*?\1)', 'g');
+
+-- Test for proper matching of non-greedy iteration (bug #11478)
+select regexp_matches('foo/bar/baz',
+                      '^([^/]+?)(?:/([^/]+?))(?:/([^/]+?))?$', '');