postgresql中的IPV6十进制到十六进制段转换函数

时间:2018-03-27 10:24:38

标签: sql postgresql plpgsql ipv6 postgresql-9.3

我正在尝试将十进制数42540528727106962370088967606034759680转换为IPV6十六进制地址分段

  

2001:200:102:200:F:FF:0:0

但我正在

  

2001:200:102:fbd9:4195:ff:0:0

来自实际结果的第五和第六个十六进制地址段

  

200:F

与我的代码的第五和第六段转换结果不匹配

  

fbd9:4195

我的功能是

CREATE OR REPLACE FUNCTION ipv6_from_number(ip_number decimal)
  RETURNS character varying AS
$BODY$
DECLARE
    ip_1            bigint  :=null;
    ip_2            bigint  :=null;
    ip_3            bigint  :=null;
    ip_4            bigint  :=null;
    ip_5            bigint  :=null;
    ip_6            bigint  :=null;
    ip_7            bigint  :=null;
    ip_8            bigint  :=null;

BEGIN
        ip_1=TRUNC((ip_number / POWER(65536,7)));
        ip_2=TRUNC(MOD(ip_number,CAST (POWER(65536,7) as numeric)) / POWER(65536,6));
        ip_3=TRUNC(MOD(ip_number,CAST (POWER(65536,6) as numeric)) / POWER(65536,5));
        ip_4=TRUNC(MOD(ip_number,CAST (POWER(65536,5) as numeric)) / POWER(65536,4));
        ip_5=TRUNC(MOD(ip_number,CAST (POWER(65536,4) as numeric)) / POWER(65536,3));
        ip_6=TRUNC(MOD(ip_number,CAST (POWER(65536,3) as numeric)) / POWER(65536,2));
        ip_7=TRUNC(MOD(ip_number,CAST (POWER(65536,2) as numeric)) / 65536);
        ip_8=TRUNC(MOD(ip_number,65536));


        return to_hex(ip_1)||':'||to_hex(ip_2)||':'||to_hex(ip_3)||':'||to_hex(ip_4)||':'||to_hex(ip_5)||':'||to_hex(ip_6)||':'||to_hex(ip_7)||':'||to_hex(ip_8);
   END;

   $BODY$
  LANGUAGE plpgsql VOLATILE
  COST 100;

1 个答案:

答案 0 :(得分:1)

我没有足够的热情来分析你的计算中的数学错误在哪里,但这不是一些错字或类似的东西 - 我的结果很简单:

t=# with v(ip_number) as (values (42540528727106962370088967606034759680::decimal))
, p as (select generate_series(0,7) g)
select
g
, TRUNC(MOD(ip_number,CAST (POWER(2,16*(g+1)) as numeric)) / POWER(2,16*g))
, to_hex(TRUNC(MOD(ip_number,CAST (POWER(2,16*(g+1)) as numeric)) / POWER(2,16*g))::bigint)
from v
join p on true;
 g | trunc | to_hex
---+-------+--------

 0 |     0 | 0
 1 |     0 | 0
 2 |   255 | ff
 3 | 16789 | 4195
 4 | 64473 | fbd9
 5 |   258 | 102
 6 |   512 | 200
 7 |  8193 | 2001
(8 rows)

在根据this post进行操作时 - 我得到了正确的计算:

t=# WITH RECURSIVE r AS (
select 1 i, 42540528727106962370088967606034759680 ip, null::text mod, null::text ff
union
select i+1 AS i,
div(ip,16),
mod(ip,16)::text,
to_hex(mod(ip,16)::int)
from r
where ip > 0
)
SELECT *,reverse(string_agg(ff,'') over ()) FROM r;
 i  |                   ip                   | mod | ff |             reverse
----+----------------------------------------+-----+----+----------------------------------
  1 | 42540528727106962370088967606034759680 |     |    | 2001020001020200000f00ff00000000
  2 |  2658783045444185148130560475377172480 | 0   | 0  | 2001020001020200000f00ff00000000
  3 |   166173940340261571758160029711073280 | 0   | 0  | 2001020001020200000f00ff00000000
  4 |    10385871271266348234885001856942080 | 0   | 0  | 2001020001020200000f00ff00000000
  5 |      649116954454146764680312616058880 | 0   | 0  | 2001020001020200000f00ff00000000
  6 |       40569809653384172792519538503680 | 0   | 0  | 2001020001020200000f00ff00000000
  7 |        2535613103336510799532471156480 | 0   | 0  | 2001020001020200000f00ff00000000
  8 |         158475818958531924970779447280 | 0   | 0  | 2001020001020200000f00ff00000000
  9 |           9904738684908245310673715455 | 0   | 0  | 2001020001020200000f00ff00000000
 10 |            619046167806765331917107215 | 15  | f  | 2001020001020200000f00ff00000000
 11 |             38690385487922833244819200 | 15  | f  | 2001020001020200000f00ff00000000
 12 |              2418149092995177077801200 | 0   | 0  | 2001020001020200000f00ff00000000
 13 |               151134318312198567362575 | 0   | 0  | 2001020001020200000f00ff00000000
 14 |                 9445894894512410460160 | 15  | f  | 2001020001020200000f00ff00000000
 15 |                  590368430907025653760 | 0   | 0  | 2001020001020200000f00ff00000000
 16 |                   36898026931689103360 | 0   | 0  | 2001020001020200000f00ff00000000
 17 |                    2306126683230568960 | 0   | 0  | 2001020001020200000f00ff00000000
 18 |                     144132917701910560 | 0   | 0  | 2001020001020200000f00ff00000000
 19 |                       9008307356369410 | 0   | 0  | 2001020001020200000f00ff00000000
 20 |                        563019209773088 | 2   | 2  | 2001020001020200000f00ff00000000
 21 |                         35188700610818 | 0   | 0  | 2001020001020200000f00ff00000000
 22 |                          2199293788176 | 2   | 2  | 2001020001020200000f00ff00000000
 23 |                           137455861761 | 0   | 0  | 2001020001020200000f00ff00000000
 24 |                             8590991360 | 1   | 1  | 2001020001020200000f00ff00000000
 25 |                              536936960 | 0   | 0  | 2001020001020200000f00ff00000000
 26 |                               33558560 | 0   | 0  | 2001020001020200000f00ff00000000
 27 |                                2097410 | 0   | 0  | 2001020001020200000f00ff00000000
 28 |                                 131088 | 2   | 2  | 2001020001020200000f00ff00000000
 29 |                                   8193 | 0   | 0  | 2001020001020200000f00ff00000000
 30 |                                    512 | 1   | 1  | 2001020001020200000f00ff00000000
 31 |                                     32 | 0   | 0  | 2001020001020200000f00ff00000000
 32 |                                      2 | 0   | 0  | 2001020001020200000f00ff00000000
 33 |                                      0 | 2   | 2  | 2001020001020200000f00ff00000000
(33 rows)

所以你可以SELECT DISTINCT reverse来获取十六进制,我只显示中间计算来演示算法。当然你可以用smth分割结果:

t=# select regexp_split_to_array('2001020001020200000f00ff00000000', E'(?=(....)+$)');
           regexp_split_to_array
-------------------------------------------
 {2001,0200,0102,0200,000f,00ff,0000,0000}
(1 row)

甚至:

t=# select translate(regexp_split_to_array('2001020001020200000f00ff00000000', E'(?=(....)+$)')::text,',{}',':');
                translate
-----------------------------------------
 2001:0200:0102:0200:000f:00ff:0000:0000
(1 row)

或进行任何进一步的简化或以其他方式获得想要的展望