PostgreSQL customize inet_aton, inet_ntoa function in text type like MySQL.
2015-10-14 22:08
776 查看
![](http://postgres.cn/images/pg2015_conf.png)
Postgres2015全国用户大会将于11月20至21日在北京丽亭华苑酒店召开。本次大会嘉宾阵容强大,国内顶级PostgreSQL数据库专家将悉数到场,并特邀欧洲、俄罗斯、日本、美国等国家和地区的数据库方面专家助阵:
Postgres-XC项目的发起人铃木市一(SUZUKI Koichi)
Postgres-XL的项目发起人Mason Sharp
pgpool的作者石井达夫(Tatsuo Ishii)
PG-Strom的作者海外浩平(Kaigai Kohei)
Greenplum研发总监姚延栋
周正中(德哥), PostgreSQL中国用户会创始人之一
汪洋,平安科技数据库技术部经理
……
2015年度PG大象会报名地址:http://postgres2015.eventdove.com/PostgreSQL中国社区: http://postgres.cn/PostgreSQL专业1群: 3336901(已满)PostgreSQL专业2群: 100910388PostgreSQL专业3群: 150657323 |
![]() |
MySQL的类型支持比较薄弱, MySQL里面没有专门的网络数据类型, 如果要存储网络信息, 一般的做法是转换成INT类型来处理.
PostgreSQL当然没有必要这么做, 因为已经支持inet和cidr类型了, 操作极为方便. 开发人员使用PostgreSQL会比较愉快.
例如
>> | contains | inet '192.168.1/24' >> inet '192.168.1.5' |
digoal=# select inet '192.168.1/24' >> inet '192.168.2.1'; ?column? ---------- f(1 row)
digoal=# select inet '192.168.1/24' >> inet '192.168.1.100'; ?column? ---------- t(1 row)
digoal=# select inet '192.168.1.0/24' >> inet '192.168.1.100'; ?column? ---------- t(1 row)
digoal=# select inet '192.168.1.128/25' >> inet '192.168.1.100'; ?column? ---------- f(1 row)
digoal=# select inet '192.168.1.128/25' >> inet '192.168.1.129'; ?column? ---------- t(1 row)
本文模拟在没有INET或者cidr类型的时候的使用方法.
那么我这里要说的是如果你是从mysql转到pg的, 并且前期不想改代码的话, 那么就需要写一些和MySQL兼容的函数.
例如MySQL里面的inet_aton , inet_ntoa.
INET_ATON(expr
)Given the dotted-quad representation of an IPv4 network address as a string, returns an integer that represents the numeric value of the address in network byte order (big endian).
INET_ATON()returns
NULLif
it does not understand its argument.
mysql> [code]SELECT INET_ATON('10.0.5.9');
-> 167773449
[/code]
For this example, the return value is calculated as 10×2563 + 0×2562 +
5×256 + 9.
INET_ATON()may
or may not return a non-
NULLresult
for short-form IP addresses (such as
'127.1'as
a representation of
'127.0.0.1').
Because of this,
INET_ATON()a
should not be used for such addresses.
Note
To store values generated by
INET_ATON(),
use an
INT UNSIGNEDcolumn rather
than
INT,
which is signed. If you use a signed column, values corresponding to IP addresses for which the first octet is greater than 127 cannot be stored correctly. See Section 11.2.6,
“Out-of-Range and Overflow Handling”.
INET_NTOA(expr
)Given a numeric IPv4 network address in network byte order, returns the dotted-quad string representation of the address as a nonbinary string in the connection character set.
INET_NTOA()returns
NULLif
it does not understand its argument.
mysql> [code]SELECT INET_NTOA(167773449);
-> '10.0.5.9'[/code]
在PostgreSQL可以通过如下自定义函数来实现 :
create or replace function inet_aton(ip text) returns int8 as $$declare v int; res int8 := 0; i int := 3;begin foreach v in array string_to_array(ip, '.') loop res := res+v*(256^i); i := i-1; end loop; return res;end;$$ language plpgsql;
create or replace function inet_ntoa(ip int8) returns text as $$declare res text := '';begin res := res || ((ip >> 24) & (2^8-1)::int); res := res || '.' || ((ip >> 16) & (2^8-1)::int); res := res || '.' || ((ip >> 8) & (2^8-1)::int); res := res || '.' || (ip & (2^8-1)::int); return res;end;$$ language plpgsql;
测试 :
digoal=# select inet_aton('192.168.9.100'); inet_aton ------------ 3232237924(1 row)
digoal=# select inet_ntoa(3232237924); inet_ntoa --------------- 192.168.9.100(1 row)
在MySQL中假如存储了IP和子网掩码的值, 我们可以通过如下方法来判断IP是否在这个子网中.
1. 从IP和子网掩码运算得到net和broadcast, 然后转换成INT8.
2. 把要匹配的IP转换成INT8, 与第一步得到的值进行比较
例如 :
192.168.9.0/24192.168.9.128/25192.168.9.0/25192.168.0.0/16
转换成net和broadcast
192.168.9.0, 192.168.9.255192.168.9.128, 192.168.9.255192.168.9.0, 192.168.9.127192.168.0.0, 192.168.255.255
net的转换方法如下 :
digoal=# select substring('192.168.9.0/24','(.*)/\d*'); substring ------------- 192.168.9.0(1 row)
broadcast的转换方法如下 :
digoal=# select inet_ntoa((inet_aton(substring('192.168.9.0/24','(.*)/\d*')) + 2^(32-substring('192.168.9.0/24','.*/(\d*)')::int) -1)::int8); inet_ntoa --------------- 192.168.9.255(1 row)
转换成数字就可以做比较了.
digoal=# create table t1 (ip text, inet text);CREATE TABLEdigoal=# insert into t1 values ('192.168.1.1','192.168.1.128/25');INSERT 0 1digoal=# insert into t1 values ('192.168.1.10','192.168.1.0/25');INSERT 0 1digoal=# insert into t1 values ('192.168.1.129','192.168.1.128/25');INSERT 0 1digoal=# select inet, substring(inet,'(.*)/\d*') as net, inet_ntoa((inet_aton(substring(inet,'(.*)/\d*')) + 2^(32-substring(inet,'.*/(\d*)')::int) -1)::int8) as broadcast from t1; inet | net | broadcast ------------------+---------------+--------------- 192.168.1.128/25 | 192.168.1.128 | 192.168.1.255 192.168.1.0/25 | 192.168.1.0 | 192.168.1.127 192.168.1.128/25 | 192.168.1.128 | 192.168.1.255(3 rows)
查看IP是否落在IPNET范围内.
digoal=# select * from (select ip, inet, substring(inet,'(.*)/\d*') as net, inet_ntoa((inet_aton(substring(inet,'(.*)/\d*')) + 2^(32-substring(inet,'.*/(\d*)')::int) -1)::int8) as broadcast from t1) t where inet_aton(ip) > inet_aton(net) and inet_aton(ip) < inet_aton(broadcast); ip | inet | net | broadcast ---------------+------------------+---------------+--------------- 192.168.1.10 | 192.168.1.0/25 | 192.168.1.0 | 192.168.1.127 192.168.1.129 | 192.168.1.128/25 | 192.168.1.128 | 192.168.1.255(2 rows)
[参考]
1. http://dev.mysql.com/doc/refman/5.5/en/miscellaneous-functions.html#function_inet-aton
2. http://doxygen.postgresql.org/inet__aton_8c_source.html#l00054
3. http://www.postgresql.org/docs/9.3/static/datatype-net-types.html
4. http://www.postgresql.org/docs/9.3/static/functions-net.html
5. http://doxygen.postgresql.org/inet__aton_8c_source.html#l00054