Instead of doing everything in a single regex, it is better to break the regex into smaller ones and test them:
if (
/* IPv6 expanded */
REGEX_LIKE(v, '\A[a-f0-9]{1,4}(:[a-f0-9]{1,4}){7}\z', 'i')
/* IPv6 shorthand */
OR (NOT REGEX_LIKE(v, '\A(.*?[a-f0-9](:|\z)){8}', 'i')
AND REGEX_LIKE(v, '\A([a-f0-9]{1,4}(:[a-f0-9]{1,4}){0,6})?::([a-f0-9]{1,4}(:[a-f0-9]{1,4}){0,6})?\z', 'i'))
/* IPv6 dotted-quad notation, expanded */
OR REGEX_LIKE(v, '\A[a-f0-9]{1,4}(:[a-f0-9]{1,4}){5}:(25[0-5]|2[0-4][0-9]|1[0-9]{2}|[1-9]?[0-9])(\.(25[0-5]|2[0-4][0-9]|1[0-9]{2}|[1-9]?[0-9])){3}\z', 'i')
/* IPv6 dotted-quad notation, shorthand */
OR (NOT REGEX_LIKE(v, '\A(.*?[a-f0-9]:){6}', 'i')
AND REGEX_LIKE(v, '\A([a-f0-9]{1,4}(:[a-f0-9]{1,4}){0,4})?::([a-f0-9]{1,4}:){0,5}(25[0-5]|2[0-4][0-9]|1[0-9]{2}|[1-9]?[0-9])(\.(25[0-5]|2[0-4][0-9]|1[0-9]{2}|[1-9]?[0-9])){3}\z', 'i'))
) then
This only tests for IPv6. IPv4 is not allowed.
Since PL/SQL flavor doesn't have subroutine calls (?n)
, there is no choice but to expand everything out. And the lack of negative look-ahead (?!pattern)
forces us to simulate it with 2 regex testing operations.
\A
and \z
are used for matching beginning and the end of the string, since both of them are not affected by flags, and \z
behavior is the same as $
under D
mode in PCRE.