Avoid divide-by-zero in regex_selectivity() with long fixed prefix.
authorTom Lane <tgl@sss.pgh.pa.us>
Fri, 12 Feb 2021 21:26:47 +0000 (16:26 -0500)
committerTom Lane <tgl@sss.pgh.pa.us>
Fri, 12 Feb 2021 21:26:47 +0000 (16:26 -0500)
Given a regex pattern with a very long fixed prefix (approaching 500
characters), the result of pow(FIXED_CHAR_SEL, fixed_prefix_len) can
underflow to zero.  Typically the preceding selectivity calculation
would have underflowed as well, so that we compute 0/0 and get NaN.
In released branches this leads to an assertion failure later on.
That doesn't happen in HEAD, for reasons I've not explored yet,
but it's surely still a bug.

To fix, just skip the division when the pow() result is zero, so
that we'll (most likely) return a zero selectivity estimate.  In
the edge cases where "sel" didn't yet underflow, perhaps this
isn't desirable, but I'm not sure that the case is worth spending
a lot of effort on.  The results of regex_selectivity_sub() are
barely worth the electrons they're written on anyway :-(

Per report from Alexander Lakhin.  Back-patch to all supported versions.

Discussion: https://postgr.es/m/6de0a0c3-ada9-cd0c-3e4e-2fa9964b41e3@gmail.com

src/backend/utils/adt/like_support.c

index bcfbaa1c3d184e04edd08f31fe98dd0a42261786..b9144e0abb2a074e3b3d27af8159ad4847d2da3c 100644 (file)
@@ -1442,9 +1442,18 @@ regex_selectivity(const char *patt, int pattlen, bool case_insensitive,
        sel *= FULL_WILDCARD_SEL;
    }
 
-   /* If there's a fixed prefix, discount its selectivity */
+   /*
+    * If there's a fixed prefix, discount its selectivity.  We have to be
+    * careful here since a very long prefix could result in pow's result
+    * underflowing to zero (in which case "sel" probably has as well).
+    */
    if (fixed_prefix_len > 0)
-       sel /= pow(FIXED_CHAR_SEL, fixed_prefix_len);
+   {
+       double      prefixsel = pow(FIXED_CHAR_SEL, fixed_prefix_len);
+
+       if (prefixsel > 0.0)
+           sel /= prefixsel;
+   }
 
    /* Make sure result stays in range */
    CLAMP_PROBABILITY(sel);