pgtypes: Support cidr and inet

This commit is contained in:
Yorhel 2025-02-10 10:54:25 +01:00
parent b66610e25a
commit 7076714296
3 changed files with 59 additions and 2 deletions

3
FU.xs
View file

@ -1,4 +1,7 @@
#include <stdio.h>
#include <errno.h>
#include <string.h>
#include <arpa/inet.h>
#include <dlfcn.h>
#undef PERL_IMPLICIT_SYS

View file

@ -326,6 +326,55 @@ SENDFN(array) {
#undef ARRAY_MAXDIM
RECVFN(inet) { /* Also works for cidr */
char tmp[128];
if (len < 8) RERR("input data too short");
// 0: ip_family, 1: mask_bits, 2: is_cidr, 3: addrsize, 4: address
if (buf[0] == 2) { /* INET */
RLEN(8);
if (!inet_ntop(AF_INET, buf+4, tmp, sizeof(tmp)-1)) RERR("%s", strerror(errno));
} else if (buf[0] == 3) { /* INET6 */
RLEN(20);
if (!inet_ntop(AF_INET6, buf+4, tmp, sizeof(tmp)-1)) RERR("%s", strerror(errno));
} else RERR("unknown address type");
if (buf[2] || buf[1] != (buf[0] == 2 ? 32 : (char)128))
return newSVpvf("%s/%d", tmp, (unsigned char)buf[1]);
return newSVpv(tmp, 0);
}
SENDFN(inet) {
char tmp[128];
STRLEN len;
const char *in = SvPV(val, len);
if (len >= sizeof(tmp)) SERR("input too long");
char family = strchr(in, ':') ? 3 : 2;
char *wr = fustr_write_buf(out, family == 2 ? 8 : 20);
unsigned char *mask = (unsigned char*)wr+1;
wr[0] = family;
*mask = family == 2 ? 32 : 128;
wr[2] = ctx->oid == 650;
wr[3] = family == 2 ? 4 : 16;
char *slash = strchr(in, '/');
if (slash && slash - in < 100) {
memcpy(tmp, in, slash - in);
tmp[slash - in] = 0;
in = tmp;
}
if (inet_pton(family == 2 ? AF_INET : AF_INET6, in, wr+4) != 1)
SERR("invalid address");
if (slash) {
UV uv = 129;
if (!grok_atoUV(slash+1, &uv, NULL) || uv > *mask)
SERR("invalid mask");
*mask = uv;
}
}
#undef SIV
#undef RLEN
#undef RECVFN
@ -385,7 +434,7 @@ SENDFN(array) {
/* 603 box */\
/* 604 polygon */\
/* 628 line */\
/* 650 cidr */\
B( 650, "cidr", inet )\
A( 629, "_line", 628 )\
A( 651, "_cidr", 650 )\
B( 700, "float4", float4)\
@ -397,7 +446,7 @@ SENDFN(array) {
/* 790 money */\
A( 791, "_money", 790 )\
/* 829 macaddr */\
/* 869 inet */\
B( 869, "inet", inet )\
A( 1000, "_bool", 16 )\
A( 1001, "_bytea", 17 )\
A( 1002, "_char", 18 )\

View file

@ -94,6 +94,11 @@ f jsonb => \1;
v jsonpath => $_ for ('$."key"', '$."a[*]"?(@ > 2)');
f jsonpath => $_ for ('', 'hello world');
v inet => $_ for ('0.0.0.0', '0.0.0.0/12', '127.0.0.1', '192.168.0.0/16', '2001:4f8:3:ba::/64', '::ffff:1.2.3.0/120', '::');
v cidr => '0.0.0.0', '0.0.0.0/32', undef, '0.0.0.0/32';
v cidr => '::', '::/128', undef, '::/128';
f inet => $_ for ('', [], '0.0.0.0/a', '0.0.0.0/1a', '[::]', '0.0.0.0/33', '::/129', '/1', ':/1');
v 'int[]', [], undef, '{}';
v 'int[]', [1], undef, '{1}';
v 'int[]', [1,-3,undef,3], undef, '{1,-3,NULL,3}';