]> granicus.if.org Git - postgresql/commitdiff
Fix regexp_matches() handling of zero-length matches.
authorTom Lane <tgl@sss.pgh.pa.us>
Wed, 31 Jul 2013 15:31:30 +0000 (11:31 -0400)
committerTom Lane <tgl@sss.pgh.pa.us>
Wed, 31 Jul 2013 15:31:30 +0000 (11:31 -0400)
We'd find the same match twice if it was of zero length and not immediately
adjacent to the previous match.  replace_text_regexp() got similar cases
right, so adjust this search logic to match that.  Note that even though
the regexp_split_to_xxx() functions share this code, they did not display
equivalent misbehavior, because the second match would be considered
degenerate and ignored.

Jeevan Chalke, with some cosmetic changes by me.

src/backend/utils/adt/regexp.c
src/backend/utils/adt/varlena.c
src/test/regress/expected/strings.out
src/test/regress/sql/strings.sql

index 92dfbb11ce61f6ea15c3c186c7ff443d0da00335..4e79b904675c6698f55c163230ae9e306b2de832 100644 (file)
@@ -957,14 +957,13 @@ setup_regexp_matches(text *orig_str, text *pattern, text *flags,
                        break;
 
                /*
-                * Advance search position.  Normally we start just after the end of
-                * the previous match, but always advance at least one character (the
-                * special case can occur if the pattern matches zero characters just
-                * after the prior match or at the end of the string).
+                * Advance search position.  Normally we start the next search at the
+                * end of the previous match; but if the match was of zero length, we
+                * have to advance by one character, or we'd just find the same match
+                * again.
                 */
-               if (start_search < pmatch[0].rm_eo)
-                       start_search = pmatch[0].rm_eo;
-               else
+               start_search = prev_match_end;
+               if (pmatch[0].rm_so == pmatch[0].rm_eo)
                        start_search++;
                if (start_search > wide_len)
                        break;
index e1b57ba3fc71fa2cb3898503fa380ff4bc21861b..09f4e106528bcbec4733f4f1806cd5e13f456463 100644 (file)
@@ -2961,7 +2961,10 @@ replace_text_regexp(text *src_text, void *regexp,
                        break;
 
                /*
-                * Search from next character when the matching text is zero width.
+                * Advance search position.  Normally we start the next search at the
+                * end of the previous match; but if the match was of zero length, we
+                * have to advance by one character, or we'd just find the same match
+                * again.
                 */
                search_start = data_pos;
                if (pmatch[0].rm_so == pmatch[0].rm_eo)
index 1bd6772ddd41a4b8fcc059cbe708de38ee598ef4..34fa4b4097665079a684bed8e156e02c99f91259 100644 (file)
@@ -440,6 +440,64 @@ SELECT regexp_matches('foobarbequebaz', $re$barbeque$re$);
  {barbeque}
 (1 row)
 
+-- start/end-of-line matches are of zero length
+SELECT regexp_matches('foo' || chr(10) || 'bar' || chr(10) || 'bequq' || chr(10) || 'baz', '^', 'mg');
+ regexp_matches 
+----------------
+ {""}
+ {""}
+ {""}
+ {""}
+(4 rows)
+
+SELECT regexp_matches('foo' || chr(10) || 'bar' || chr(10) || 'bequq' || chr(10) || 'baz', '$', 'mg');
+ regexp_matches 
+----------------
+ {""}
+ {""}
+ {""}
+ {""}
+(4 rows)
+
+SELECT regexp_matches('1' || chr(10) || '2' || chr(10) || '3' || chr(10) || '4' || chr(10), '^.?', 'mg');
+ regexp_matches 
+----------------
+ {1}
+ {2}
+ {3}
+ {4}
+ {""}
+(5 rows)
+
+SELECT regexp_matches(chr(10) || '1' || chr(10) || '2' || chr(10) || '3' || chr(10) || '4' || chr(10), '.?$', 'mg');
+ regexp_matches 
+----------------
+ {""}
+ {1}
+ {""}
+ {2}
+ {""}
+ {3}
+ {""}
+ {4}
+ {""}
+ {""}
+(10 rows)
+
+SELECT regexp_matches(chr(10) || '1' || chr(10) || '2' || chr(10) || '3' || chr(10) || '4', '.?$', 'mg');
+ regexp_matches 
+----------------
+ {""}
+ {1}
+ {""}
+ {2}
+ {""}
+ {3}
+ {""}
+ {4}
+ {""}
+(9 rows)
+
 -- give me errors
 SELECT regexp_matches('foobarbequebaz', $re$(bar)(beque)$re$, 'gz');
 ERROR:  invalid regexp option: "z"
index 6ef446308b56f646736f21737193184564ac003a..198b85c8df9caf745703b0f2670e446f78eee26e 100644 (file)
@@ -158,6 +158,13 @@ SELECT regexp_matches('foobarbequebaz', $re$(bar)(.+)?(beque)$re$);
 -- no capture groups
 SELECT regexp_matches('foobarbequebaz', $re$barbeque$re$);
 
+-- start/end-of-line matches are of zero length
+SELECT regexp_matches('foo' || chr(10) || 'bar' || chr(10) || 'bequq' || chr(10) || 'baz', '^', 'mg');
+SELECT regexp_matches('foo' || chr(10) || 'bar' || chr(10) || 'bequq' || chr(10) || 'baz', '$', 'mg');
+SELECT regexp_matches('1' || chr(10) || '2' || chr(10) || '3' || chr(10) || '4' || chr(10), '^.?', 'mg');
+SELECT regexp_matches(chr(10) || '1' || chr(10) || '2' || chr(10) || '3' || chr(10) || '4' || chr(10), '.?$', 'mg');
+SELECT regexp_matches(chr(10) || '1' || chr(10) || '2' || chr(10) || '3' || chr(10) || '4', '.?$', 'mg');
+
 -- give me errors
 SELECT regexp_matches('foobarbequebaz', $re$(bar)(beque)$re$, 'gz');
 SELECT regexp_matches('foobarbequebaz', $re$(barbeque$re$);