Java функция для преобразования CIDR в регулярные выражения - PullRequest
1 голос
/ 28 февраля 2020

У меня есть база данных oracle, в которой есть несколько IP-адресов в виде строк (varchar2). Я хотел бы иметь возможность найти те, которые содержатся в данном блоке CIDR. Поскольку это не какой-то нативный тип ip, я не могу использовать какой-либо нативный механизм поиска IP. Я думал, что можно сгенерировать регулярное выражение из cidr (например: «10.1.0.0/10»), которое найдет правильные значения.

Любой, кто имеет функцию или знает о библиотеке в java что может сделать это? Или какое-нибудь альтернативное решение?

Oracle 12.1.0.2

1 Ответ

1 голос
/ 01 марта 2020

OP (Original Poster) упоминает функцию "inet_aton", которая может преобразовывать строковое представление адреса IPv4 в соответствующее ему целочисленное значение. Увы, такой функции нет в Oracle SQL и / или в PL / SQL.

В приведенном ниже ответе я покажу (тривиально!) Способ, которым такая функция может быть написана в PL / SQL. Затем я продемонстрирую, как его можно использовать для решения проблемы ОП.

Примечание ОП упоминает "регулярные выражения" как возможный подход к решению той же проблемы. Я сомневаюсь, что что-нибудь в этом роде будет настолько же эффективным, как то, что я предлагаю ниже. Если блоки являются классными (имеется в виду, если суффикс кратен 8), то действительно, очень просто написать условие regexp_like (нам нужно сопоставить только первую, две или три "части" из четырех часть IP-адреса). Однако для бесклассовых блоков проблема становится намного более сложной. Конечная заметка

РЕДАКТИРОВАТЬ Для чего я только что попробовал функцию преобразования в таблице с 1,6 миллиона IP-адресов. Он конвертировал их все в числовой формат c за 0,3 секунды. END EDIT

В коде я создаю функцию как отдельный объект (функцию). При желании функция может быть определена в предложении WITH в том же запросе, в котором она используется. Это может привести к немного лучшей производительности. (Можно также использовать формулу из тела функции напрямую в запросе select, поэтому нигде нет вызовов функций, но прирост производительности, вероятно, будет минимальным.)

Если важна производительность, OP может добавить функциональный индекс в таблицу, в которой хранятся IP-адреса; затем для относительно небольших блоков CIDR запрос должен быть очень быстрым, если фильтр достаточно избирателен.

Вот одна из реализаций функции для преобразования адреса IPv4 из формата из четырех строк в формат целое число:

create or replace function ip_str_to_num (s varchar2) return number
  deterministic
as
  pragma udf;
begin
  return 
  ((to_number(substr(s,1,instr(s,'.',1,1)-1))*256+ 
    to_number(substr(s,instr(s,'.',1,1)+1,instr(s,'.',1,2)-instr(s,'.',1,1)-1))
   )*256+
   to_number(substr(s,instr(s,'.',1,2)+1,instr(s,'.',1,3)-instr(s,'.',1,2)-1))
  )*256+to_number(substr(s,instr(s,'.',1,3)+1));
end;
/

Примечание, в частности pragma udf - доступно только с Oracle 12.1; это заставляет функцию работать быстрее, если она используется только в контексте SQL.

Далее приведен пример использования этой функции. Я создаю очень маленькую таблицу «доступных адресов». Затем я показываю запрос, где я жестко кодирую блок CIDR (в общем случае его следует заменить на переменную связывания); Я показываю, как мы можем использовать функцию преобразования, чтобы генерировать минимальный и максимальный адреса в блоке в формате цифр c, а затем присоединяюсь к таблице адресов, чтобы найти только те адреса, которые принадлежат блоку.

Данные испытаний:

create table ip_address_list(ip_address varchar2(19));

insert into ip_address_list
  select '123.33.2.234' from dual union all
  select '230.0.0.1'    from dual union all
  select '43.233.83.2'  from dual union all
  select '128.233.2.8'  from dual union all
  select '72.120.0.1'   from dual union all
  select '128.232.1.64' from dual
;

commit;

Запрос и результат:

with
  inputs(cidr_block) as (
    select '128.224.0.0/10' from dual
  )
, prep(min_addr, suffix) as (
    select ip_str_to_num(substr(cidr_block, 1, instr(cidr_block, '/') - 1)), 
           substr(cidr_block, instr(cidr_block, '/') + 1)
    from   inputs
  )
, address_range(min_addr, max_addr) as (
    select min_addr, min_addr + power(2, 32 - suffix) - 1
    from   prep
  )
select ip_address 
from   ip_address_list join address_range 
                       on ip_str_to_num(ip_address) between min_addr and max_addr
order  by ip_str_to_num(ip_address)
;

IP_ADDRESS         
-------------------
128.232.1.64
128.233.2.8
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...