2013-05-16 20 views
5

我在以下布局中存储的网络中有大量子网。这用作主表来存储资源,这些资源将用于定期通过Python脚本自动探测状态。PostgreSQL将cidr扩展为单独的地址

CREATE TEMP TABLE tmp_networks (
    network cidr PRIMARY KEY 
); 

让我们假设它充斥着这些价值观用于演示的缘故:

  • 10.0.0.0/8
  • 10.0.1.0/24
  • 192.168.0.0/24

当我运行该脚本时,Python脚本将执行以下查询以删除任何重叠:

SELECT network 
    FROM tmp_networks 
    WHERE NOT EXISTS (
     SELECT network 
     FROM tmp_networks n 
     WHERE n.network >> tmp_networks.network 
); 

这很好,除了一个小问题;我也有一份应该从工作中排除的个人地址列表。这也是在数据库中的表:

CREATE TEMP TABLE tmp_except (
    address inet PRIMARY KEY 
); 

让我们假设这包含以下地址:

  • 10.0.0.100
  • 192.168.0.10

现在,我已经失败找到一个从数据库输出中删除这些特定地址的好方法。在我的思想,解决办法是这样的:

  • 选择所有子网
  • 如有异常地址在子网内发现,分裂子网成更小的碎片,直到唯一的例外地址可以被删除,所有其他地址仍然

我试图调查是否有这样的事情可以在纯PostgreSQL中做,但没有找到任何方法来解决这个问题。任何指针应该如何解决?

+0

您对查询的期望结果是什么?它总是*地址(/ 32)*还是它*网络地址如果可能的话,只有地址,如果有例外*? – Beryllium

+0

嗨,我除了网络地址,如果可能的话OR/32如果没有更大的块存在。 – agnsaft

回答

5

我会用两个函数来解决这个问题。第一个函数接受一个cidr和一个异常地址,并返回一组等于原始cidr的cidrs减去异常地址。该函数通过将cidr分成两半来工作,然后递归地从异常地址的一半中移除异常地址。更复杂的算法可以避免一些不必要的分割。简单的功能如下:

CREATE OR REPLACE FUNCTION split_cidr(net cidr, exc inet) returns setof cidr language plpgsql AS $$ 
DECLARE 
    r cidr; 
    lower cidr; 
    upper cidr; 
BEGIN 
    IF masklen(net) >= 32 THEN RETURN; END IF; 
    lower = set_masklen(net, masklen(net)+1); 
    upper = set_masklen((lower | ~ netmask(lower)) + 1, masklen(lower)); 
    IF exc << upper THEN 
    RETURN NEXT lower; 
    FOR r IN SELECT * from split_cidr(upper, exc) 
    LOOP RETURN NEXT r; 
    END LOOP; 
    ELSE 
    FOR r IN SELECT * from split_cidr(lower, exc) 
    LOOP RETURN NEXT r; 
    END LOOP; 
    RETURN NEXT upper; 
    END IF; 
    RETURN; 
END $$; 

有了这些功能,我们可以再通过将其应用到包含例外地址的网络的网络列表进行迭代。以下功能将网络地址列表分为包含异常和不包含异常的网络地址。那些没有返回的,那些具有上述功能的应用。这不涉及网络包含多于异常地址的情况。

CREATE OR REPLACE FUNCTION DOIT() RETURNS Setof cidr language plpgsql AS $$ 
DECLARE 
r cidr; 
x cidr; 
z inet; 
BEGIN 
-- these are the rows where the network has no exceptions 
FOR r in SELECT network FROM tmp_networks n WHERE NOT EXISTS (
    SELECT address FROM tmp_except WHERE address << n.network) 
LOOP RETURN NEXT r; 
END LOOP; 

-- these are the rows where the network has an exception 
FOR r,z in SELECT network, address from tmp_networks full join tmp_except on true where address << network 
LOOP 
    FOR x IN SELECT * FROM split_cidr(r, z) 
    LOOP RETURN NEXT x; 
    END LOOP; 
END LOOP; 
END $$; 

我将通过修改split_cidr采取异常地址阵列而不是单个异常地址,然后聚集用于每个网络的异常到一个数组并调用split_cidr_array为接近每个网络的多个例外的地址的情况下网络及其例外情况。