Ilya Kovalenko posted some code at in a thread starting at

  http://archives.postgresql.org/pgsql-hackers/2005-04/msg00417.php

which lead to the TODO item:

* Allow INET + INT4 to increment the host part of the address, or
  throw an error on overflow

I think that the naively coded function attached does what is needed, e.g.,

CREATE OR REPLACE FUNCTION inet_inc(inet, int4)
        RETURNS inet
        AS '/tmp/inet.so','inet_inc'
        LANGUAGE C STRICT;

CREATE OPERATOR + (
        leftarg = inet,
        rightarg = int4,
        procedure = inet_inc
);

test=# select '192.168.0.1/24'::inet + 300;
ERROR:  Increment (300) too big for network (/24)
test=# select '192.168.0.1/24'::inet + 254;
     ?column?     
------------------
 192.168.0.255/24
(1 row)

test=# select '192.168.0.1/24'::inet + 255;
ERROR:  Increment (255) takes address (192.168.0.1) out of its network (/24)
test=# select '192.168.0.1/24'::inet + -2;
ERROR:  Increment (-2) takes address (192.168.0.1) out of its network (/24)
test=# select '255.255.255.254/0'::inet + 2;
ERROR:  Increment (2) takes address (255.255.255.254) out of its network (/0)

and just for fun:

create table list (
        host inet
);

insert into list values ('192.168.0.1/24');
insert into list values ('192.168.0.2/24');
insert into list values ('192.168.0.4/24');
insert into list values ('192.168.0.5/24');
insert into list values ('192.168.0.6/24');
insert into list values ('192.168.0.8/24');
insert into list values ('192.168.0.9/24');
insert into list values ('192.168.0.10/24');
insert into list values ('192.168.1.1/24');
insert into list values ('192.168.1.3/24');

select host+1 from list
 where host+1 <<= '192.168.1.0/24'
   and not exists
       ( select 1
           from list
          where host=host+1
            and host << '192.168.1.0/24' )
 limit 1;



If you agree that this is the right thing, I can code it less
naively, (Ilya rightly uses ntohl/htonl), create the operator's
commutator, provide a patch which makes it a built-in, and some
obvious documentation.

Cheers,

Patrick
/* From the TODO:
 *    Allow INET + INT4 to increment the host part of the address, or
 *    throw an error on overflow
 */

#include "postgres.h"

#include <sys/socket.h>

#include "fmgr.h"
#include "utils/inet.h"

PG_FUNCTION_INFO_V1(inet_inc);

Datum
inet_inc(PG_FUNCTION_ARGS)
{
        inet    *in  = PG_GETARG_INET_P(0), *out;
        int32    inc = PG_GETARG_INT32(1);
        inet_struct *src, *dst;
        uint32   netmask, host, newhost;
        int              i;

        src = (inet_struct *)VARDATA(in);
        if (src->family != PGSQL_AF_INET)
                ereport(ERROR,
                        (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
                         errmsg("Function \"inet_inc\" only supports AF_INET "
                                                                         
"addresses")));

        /* avoid int32 overflow when bits == 0 */
        netmask = (src->bits == 0) ? 0 : (~((1 << (32 - src->bits)) - 1));

        /* if (inc doesn't fit in src->bits) overflow */
        if ((abs(inc) & ~netmask) != abs(inc))
                ereport(ERROR,
                        (errcode(ERRCODE_DATA_EXCEPTION),
                         errmsg("Increment (%d) too big for network (/%d)",
                                                                         inc, 
src->bits)));

        /* can do this with htonl/ntohl */
        host = 0;
        for (i=0; i<4; ++i)
                host |= src->ipaddr[i] << (8 * (3-i));

        newhost = host + inc;

        if (((host & netmask) != (newhost & netmask))
                || (inc>0 && newhost<host)
                || (inc<0 && newhost>host))
                ereport(ERROR,
                        (errcode(ERRCODE_DATA_EXCEPTION),
                         errmsg("Increment (%d) takes address (%d.%d.%d.%d) out 
of its "
                                "network (/%d)", inc,
                                src->ipaddr[0], src->ipaddr[1], src->ipaddr[2],
                                src->ipaddr[3], src->bits)));

        out = (inet *)palloc0(VARHDRSZ + sizeof(inet_struct));

        dst = (inet_struct *)VARDATA(out);

        dst->family = src->family;
        dst->bits   = src->bits;
        dst->type   = src->type;
        for (i=0; i<4; ++i)
                dst->ipaddr[i] = (newhost >> (8 * (3-i))) & 0xff;
        for (i=4; i<16; ++i)
                dst->ipaddr[i] = 0;

        VARATT_SIZEP(out) = VARHDRSZ + sizeof(dst->family) + sizeof(dst->bits)
                + sizeof(dst->type) + 4;

        PG_RETURN_INET_P(out);
}
---------------------------(end of broadcast)---------------------------
TIP 2: Don't 'kill -9' the postmaster

Reply via email to