Re: CIDR/IP types. Was: [GENERAL] big numbers - Mailing list pgsql-hackers

From Tom Ivar Helbekkmo
Subject Re: CIDR/IP types. Was: [GENERAL] big numbers
Date
Msg-id 86yarubvri.fsf@athene.nhh.no
Whole thread Raw
In response to Re: CIDR/IP types. Was: [GENERAL] big numbers  (Bruce Momjian <maillist@candle.pha.pa.us>)
Responses Re: CIDR/IP types. Was: [GENERAL] big numbers
Re: CIDR/IP types. Was: [GENERAL] big numbers
List pgsql-hackers
Bruce Momjian wrote:

> When do you think you can you look over both versions, and send me
> one good version to work with?

I've got something working right now, by taking what I was using, and
changing it to work the way Paul's code does, with some enhancements:
My code is ready for storing both IPV4 and IPV6 at the same time with
variable length storage in the data base, and it's got Aleksei's index
integration in place.  The type name is still "ipaddr" -- but that's
easy to change, of course.

> Even if you just say, "Paul's is better, throw out ip_and_mac", that
> is all we need.

I am definitely *not* going to say "I can do this better than Vixie".

The way I feel about this right now is that Paul's code is better than
what I originally submitted, and better than Aleksei's improvements on
that code.  However, what I currently run has certain improvements
over all of those versions, and I kind of like the way it's going...

I'll append it below.  Take a look, and let me know what you think.

Oh, and a correction:  Paul Vixie wrote:

> the ip_and_mac type is host-only but has an unfortunate bridging
> between layer 3 and layer 2.

No, it was (and is) two different types, just packaged in the same
directory because I thought they conceptually belonged together.

Anyway, I'm appending a shar of what I've got right now.  It's only
minimally tested so far, and I'm *not* a professional programmer.
I'm basically just having a lot of fun with this, while it is at the
same time useful for me in my work, and if what I write can be of use
to others, that's great!  :-)

-tih
--
Popularity is the hallmark of mediocrity.  --Niles Crane, "Frasier"

# This is a shell archive.  Save it in a file, remove anything before
# this line, and then unpack it by entering "sh file".  Note, it may
# create directories; files and directories will be owned by you and
# have default permissions.
#
# This archive contains:
#
#    Makefile
#    README
#    inet_net_ntop.c
#    inet_net_pton.c
#    ip.c
#    ip.sql.in
#    mac.c
#    mac.h
#    mac.sql.in
#    test.sql
#
echo x - Makefile
sed 's/^X//' >Makefile << 'END-of-Makefile'
X#
X#    PostgreSQL types for IP and MAC addresses
X#
X#    $Id: Makefile,v 1.4 1998/09/08 12:23:30 tih Exp $
X
XPGINST=/usr/local/pgsql
XTARGET=/u/tih/databases
X
Xall: ip.so mac.so ip.sql mac.sql
X
Xip.so: ip.o inet_net_ntop.o inet_net_pton.o
X    ld -Bshareable -o ip.so ip.o inet_net_ntop.o inet_net_pton.o
X
Xip.o: ip.c
X    cc -g -O -fPIC -I${PGINST}/include -c ip.c
X
Xinet_net_ntop.o: inet_net_ntop.c
X    cc -g -O -fPIC -c inet_net_ntop.c
X
Xinet_net_pton.o: inet_net_pton.c
X    cc -g -O -fPIC -c inet_net_pton.c
X
Xmac.so: mac.o
X    ld -Bshareable -o mac.so mac.o
X
Xmac.o: mac.c mac.h
X    cc -g -O -fPIC -I${PGINST}/include -c mac.c
X
Xip.sql: ip.sql.in
X    cat ip.sql.in | sed s@TARGET@${TARGET}/modules@ > ip.sql
X
Xmac.sql: mac.sql.in
X    cat mac.sql.in | sed s@TARGET@${TARGET}/modules@ > mac.sql
X
Xinstall: ip.so mac.so
X    install -c ip.sql ip.so mac.sql mac.so ${TARGET}/modules
X
Xclean:
X    rm -f *.o *.so ip.sql mac.sql
X
XFILES=Makefile README inet_net_ntop.c inet_net_pton.c \
X    ip.c ip.sql.in mac.c mac.h mac.sql.in test.sql
X
Xip+mac.shar: ${FILES}
X    shar ${FILES} > ip+mac.shar
X
X#
X#    eof
X#
END-of-Makefile
echo x - README
sed 's/^X//' >README << 'END-of-README'
XPostgreSQL type extensions for IP and MAC addresses.
X---------------------------------------------------
X
X$Id: README,v 1.2 1998/09/08 12:10:22 tih Exp $
X
XI needed to record IP and MAC level ethernet addresses in a data
Xbase, and I really didn't want to store them as plain strings, with
Xno enforced error checking, so I put together the accompanying code
Xas my first experiment with adding a data type to PostgreSQL.  I
Xthen thought that this might be useful to others, both directly and
Xas a very simple example of how to do this sort of thing, so I
Xsubmitted it to the PostgreSQL project for inclusion in the contrib
Xdirectory.  Since then, that directory has been modified to contain
XAleksei Roudnev's implementation, which is based on mine.
X
XFor those who have seen my previous contribution of these types, note
Xthat much has changed: I've modified the IP address type to work the
Xway Paul Vixie did with his CIDR type.  In fact, I've pretty much just
Xstolen his solution, modifying it into my framework in such a way as
Xto facilitate the addition of IPV6 handling code in the future.  I've
Xpretty much ignored Aleksei's C code, but I've added his SQL code to
Xenter the necessary operators into the various system tables needed to
Xmake the types indexable.
X
XIP addresses are implemented as a struct of fixed in-memory length,
Xbut variable on-disk storage size.  For IPV4, it contains the address
Xfamily (AF_INET), the CIDR prefix length and four byte address.  For
XIPV6, the address family will be different, and the address longer.
X
XThe external representation of an IP address generally looks like
X'158.37.96.15/32'.  This address happens to be part of a subnet where
XI work; '158.37.96.0/24', which itself is a part of the larger subnet
Xallocated to our site, which is '158.37.96.0/21', which again, if you
Xgo by the old book, is part of the class "B" net '158.37.0.0/16'.
X
XInput and output functions are supplied, along with the "normal" <,
X<=, =, >=, > and <> operators, which all do what you expect.  In
Xaddition, there are operators to check for networks or addresses being
Xsubnets of or addresses contained within other networks.  << tests
Xwhether the left operand is contained within the right, <<= includes
Xequality, >> and >>= do the same things the opposite way.
X
XThe input and output functions use routines from Paul Vixie's BIND,
Xand I've snarfed the source files inet_net_ntop.c and inet_net_pton.c
Xdirectly from a recent distribution of that code.  They are included
Xhere to avoid the need to fetch and install the BIND libraries to be
Xable to use this code.  IANAL, but it looks from the copyright
Xmessages in the files as if this should be acceptable.  Read the
Xdocumentation in inet_net_pton.c to see the legal input formats.
X
XMAC level ethernet addresses are implemented as a 6 byte struct that
Xcontains the address as unsigned chars.  Several input forms are
Xaccepted; the following are all the same address: '08002b:010203',
X'08002b-010203', '0800.2b01.0203', '08-00-2b-01-02-03' and
X'08:00:2b:01:02:03'.  Upper and lower case is accepted for the digits
X'a' through 'f'.  Output is always in the latter of the given forms.
X
XAs with IP addresses, input and output functions are supplied as well
Xas the "normal" operators, which do what you expect.  As an extra
Xfeature, a function macaddr_manuf() is defined, which returns the name
Xof the manufacturer as a string.  This is currently held in a
Xhard-coded struct internal to the C module -- it might be smarter to
Xput this information into an actual data base table, and look up the
Xmanufacturer there.
X
XMany thanks to Aleksei Roudnev and Paul Vixie for their fine work!
X
XI don't know what changes are needed to the Makefile for other systems
Xthan the one I'm running (NetBSD 1.3), but anyway: to install on a BSD
Xsystem: fix the path names in the Makefile if you need to, then make,
Xmake install, slurp the SQL files into psql or whatever, and you're
Xoff.  Enjoy!
X
XBergen, Norway, 1998-08-09, Tom Ivar Helbekkmo (tih@nhh.no).
END-of-README
echo x - inet_net_ntop.c
sed 's/^X//' >inet_net_ntop.c << 'END-of-inet_net_ntop.c'
X/*
X * Copyright (c) 1996 by Internet Software Consortium.
X *
X * Permission to use, copy, modify, and distribute this software for any
X * purpose with or without fee is hereby granted, provided that the above
X * copyright notice and this permission notice appear in all copies.
X *
X * THE SOFTWARE IS PROVIDED "AS IS" AND INTERNET SOFTWARE CONSORTIUM DISCLAIMS
X * ALL WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES
X * OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL INTERNET SOFTWARE
X * CONSORTIUM BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL
X * DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR
X * PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS
X * ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS
X * SOFTWARE.
X */
X
X#if defined(LIBC_SCCS) && !defined(lint)
Xstatic const char rcsid[] = "$Id: inet_net_ntop.c,v 8.2 1996/08/08 06:54:44 vixie Exp $";
X#endif
X
X#include <sys/types.h>
X#include <sys/socket.h>
X#include <netinet/in.h>
X#include <arpa/inet.h>
X
X#include <errno.h>
X#include <stdio.h>
X#include <string.h>
X#include <stdlib.h>
X
X#ifdef SPRINTF_CHAR
X# define SPRINTF(x) strlen(sprintf/**/x)
X#else
X# define SPRINTF(x) ((size_t)sprintf x)
X#endif
X
Xstatic char *    inet_net_ntop_ipv4 __P((const u_char *src, int bits,
X                    char *dst, size_t size));
X
X/*
X * char *
X * inet_net_ntop(af, src, bits, dst, size)
X *    convert network number from network to presentation format.
X *    generates CIDR style result always.
X * return:
X *    pointer to dst, or NULL if an error occurred (check errno).
X * author:
X *    Paul Vixie (ISC), July 1996
X */
Xchar *
Xinet_net_ntop(af, src, bits, dst, size)
X    int af;
X    const void *src;
X    int bits;
X    char *dst;
X    size_t size;
X{
X    switch (af) {
X    case AF_INET:
X        return (inet_net_ntop_ipv4(src, bits, dst, size));
X    default:
X        errno = EAFNOSUPPORT;
X        return (NULL);
X    }
X}
X
X/*
X * static char *
X * inet_net_ntop_ipv4(src, bits, dst, size)
X *    convert IPv4 network number from network to presentation format.
X *    generates CIDR style result always.
X * return:
X *    pointer to dst, or NULL if an error occurred (check errno).
X * note:
X *    network byte order assumed.  this means 192.5.5.240/28 has
X *    0x11110000 in its fourth octet.
X * author:
X *    Paul Vixie (ISC), July 1996
X */
Xstatic char *
Xinet_net_ntop_ipv4(src, bits, dst, size)
X    const u_char *src;
X    int bits;
X    char *dst;
X    size_t size;
X{
X    char *odst = dst;
X    char *t;
X    u_int m;
X    int b;
X
X    if (bits < 0 || bits > 32) {
X        errno = EINVAL;
X        return (NULL);
X    }
X    if (bits == 0) {
X        if (size < sizeof "0")
X            goto emsgsize;
X        *dst++ = '0';
X        *dst = '\0';
X    }
X
X    /* Format whole octets. */
X    for (b = bits / 8; b > 0; b--) {
X        if (size < sizeof "255.")
X            goto emsgsize;
X        t = dst;
X        dst += SPRINTF((dst, "%u", *src++));
X        if (b > 1) {
X            *dst++ = '.';
X            *dst = '\0';
X        }
X        size -= (size_t)(dst - t);
X    }
X
X    /* Format partial octet. */
X    b = bits % 8;
X    if (b > 0) {
X        if (size < sizeof ".255")
X            goto emsgsize;
X        t = dst;
X        if (dst != odst)
X            *dst++ = '.';
X        m = ((1 << b) - 1) << (8 - b);
X        dst += SPRINTF((dst, "%u", *src & m));
X        size -= (size_t)(dst - t);
X    }
X
X    /* Format CIDR /width. */
X    if (size < sizeof "/32")
X        goto emsgsize;
X    dst += SPRINTF((dst, "/%u", bits));
X    return (odst);
X
X emsgsize:
X    errno = EMSGSIZE;
X    return (NULL);
X}
END-of-inet_net_ntop.c
echo x - inet_net_pton.c
sed 's/^X//' >inet_net_pton.c << 'END-of-inet_net_pton.c'
X/*
X * Copyright (c) 1996 by Internet Software Consortium.
X *
X * Permission to use, copy, modify, and distribute this software for any
X * purpose with or without fee is hereby granted, provided that the above
X * copyright notice and this permission notice appear in all copies.
X *
X * THE SOFTWARE IS PROVIDED "AS IS" AND INTERNET SOFTWARE CONSORTIUM DISCLAIMS
X * ALL WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES
X * OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL INTERNET SOFTWARE
X * CONSORTIUM BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL
X * DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR
X * PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS
X * ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS
X * SOFTWARE.
X */
X
X#if defined(LIBC_SCCS) && !defined(lint)
Xstatic const char rcsid[] = "$Id: inet_net_pton.c,v 8.3 1996/11/11 06:36:52 vixie Exp $";
X#endif
X
X#include <sys/types.h>
X#include <sys/socket.h>
X#include <netinet/in.h>
X#include <arpa/inet.h>
X
X#include <assert.h>
X#include <ctype.h>
X#include <errno.h>
X#include <stdio.h>
X#include <string.h>
X#include <stdlib.h>
X
X#ifdef SPRINTF_CHAR
X# define SPRINTF(x) strlen(sprintf/**/x)
X#else
X# define SPRINTF(x) ((size_t)sprintf x)
X#endif
X
Xstatic int    inet_net_pton_ipv4 __P((const char *src, u_char *dst,
X                    size_t size));
X
X/*
X * static int
X * inet_net_pton(af, src, dst, size)
X *    convert network number from presentation to network format.
X *    accepts hex octets, hex strings, decimal octets, and /CIDR.
X *    "size" is in bytes and describes "dst".
X * return:
X *    number of bits, either imputed classfully or specified with /CIDR,
X *    or -1 if some failure occurred (check errno).  ENOENT means it was
X *    not a valid network specification.
X * author:
X *    Paul Vixie (ISC), June 1996
X */
Xint
Xinet_net_pton(af, src, dst, size)
X    int af;
X    const char *src;
X    void *dst;
X    size_t size;
X{
X    switch (af) {
X    case AF_INET:
X        return (inet_net_pton_ipv4(src, dst, size));
X    default:
X        errno = EAFNOSUPPORT;
X        return (-1);
X    }
X}
X
X/*
X * static int
X * inet_net_pton_ipv4(src, dst, size)
X *    convert IPv4 network number from presentation to network format.
X *    accepts hex octets, hex strings, decimal octets, and /CIDR.
X *    "size" is in bytes and describes "dst".
X * return:
X *    number of bits, either imputed classfully or specified with /CIDR,
X *    or -1 if some failure occurred (check errno).  ENOENT means it was
X *    not an IPv4 network specification.
X * note:
X *    network byte order assumed.  this means 192.5.5.240/28 has
X *    0x11110000 in its fourth octet.
X * author:
X *    Paul Vixie (ISC), June 1996
X */
Xstatic int
Xinet_net_pton_ipv4(src, dst, size)
X    const char *src;
X    u_char *dst;
X    size_t size;
X{
X    static const char
X        xdigits[] = "0123456789abcdef",
X        digits[] = "0123456789";
X    int n, ch, tmp, dirty, bits;
X    const u_char *odst = dst;
X
X    ch = *src++;
X    if (ch == '0' && (src[0] == 'x' || src[0] == 'X')
X        && isascii(src[1]) && isxdigit(src[1])) {
X        /* Hexadecimal: Eat nybble string. */
X        if (size <= 0)
X            goto emsgsize;
X        *dst = 0, dirty = 0;
X        src++;    /* skip x or X. */
X        while ((ch = *src++) != '\0' &&
X               isascii(ch) && isxdigit(ch)) {
X            if (isupper(ch))
X                ch = tolower(ch);
X            n = strchr(xdigits, ch) - xdigits;
X            assert(n >= 0 && n <= 15);
X            *dst |= n;
X            if (!dirty++)
X                *dst <<= 4;
X            else if (size-- > 0)
X                *++dst = 0, dirty = 0;
X            else
X                goto emsgsize;
X        }
X        if (dirty)
X            size--;
X    } else if (isascii(ch) && isdigit(ch)) {
X        /* Decimal: eat dotted digit string. */
X        for (;;) {
X            tmp = 0;
X            do {
X                n = strchr(digits, ch) - digits;
X                assert(n >= 0 && n <= 9);
X                tmp *= 10;
X                tmp += n;
X                if (tmp > 255)
X                    goto enoent;
X            } while ((ch = *src++) != '\0' &&
X                 isascii(ch) && isdigit(ch));
X            if (size-- <= 0)
X                goto emsgsize;
X            *dst++ = (u_char) tmp;
X            if (ch == '\0' || ch == '/')
X                break;
X            if (ch != '.')
X                goto enoent;
X            ch = *src++;
X            if (!isascii(ch) || !isdigit(ch))
X                goto enoent;
X        }
X    } else
X        goto enoent;
X
X    bits = -1;
X    if (ch == '/' && isascii(src[0]) && isdigit(src[0]) && dst > odst) {
X        /* CIDR width specifier.  Nothing can follow it. */
X        ch = *src++;    /* Skip over the /. */
X        bits = 0;
X        do {
X            n = strchr(digits, ch) - digits;
X            assert(n >= 0 && n <= 9);
X            bits *= 10;
X            bits += n;
X        } while ((ch = *src++) != '\0' &&
X             isascii(ch) && isdigit(ch));
X        if (ch != '\0')
X            goto enoent;
X        if (bits > 32)
X            goto emsgsize;
X    }
X
X    /* Firey death and destruction unless we prefetched EOS. */
X    if (ch != '\0')
X        goto enoent;
X
X    /* If nothing was written to the destination, we found no address. */
X    if (dst == odst)
X        goto enoent;
X    /* If no CIDR spec was given, infer width from net class. */
X    if (bits == -1) {
X        if (*odst >= 240)    /* Class E */
X            bits = 32;
X        else if (*odst >= 224)    /* Class D */
X            bits = 4;
X        else if (*odst >= 192)    /* Class C */
X            bits = 24;
X        else if (*odst >= 128)    /* Class B */
X            bits = 16;
X        else            /* Class A */
X            bits = 8;
X        /* If imputed mask is narrower than specified octets, widen. */
X        if (bits >= 8 && bits < ((dst - odst) * 8))
X            bits = (dst - odst) * 8;
X    }
X    /* Extend network to cover the actual mask. */
X    while (bits > ((dst - odst) * 8)) {
X        if (size-- <= 0)
X            goto emsgsize;
X        *dst++ = '\0';
X    }
X    return (bits);
X
X enoent:
X    errno = ENOENT;
X    return (-1);
X
X emsgsize:
X    errno = EMSGSIZE;
X    return (-1);
X}
END-of-inet_net_pton.c
echo x - ip.c
sed 's/^X//' >ip.c << 'END-of-ip.c'
X/*
X *    PostgreSQL type definitions for IP addresses.  This
X *    is for IP V4 CIDR notation, but prepared for V6: just
X *    add the necessary bits where the comments indicate.
X *
X *    $Id: ip.c,v 1.2 1998/09/08 12:10:36 tih Exp $
X */
X
X#include <sys/types.h>
X#include <sys/socket.h>
X
X#include <stdio.h>
X#include <errno.h>
X
X#include <netinet/in.h>
X#include <arpa/inet.h>
X
X#include <postgres.h>
X#include <utils/palloc.h>
X
X/*
X *    This is the internal storage format for IP addresses:
X */
X
Xtypedef struct {
X  unsigned char family;
X  unsigned char bits;
X  union {
X    u_int32_t ipv4_addr;    /* network byte order */
X                /* add IPV6 address type here */
X  } addr;
X} ipaddr_struct;
X
Xtypedef struct varlena ipaddr;
X
X/*
X *    Access macros.  Add IPV6 support.
X */
X
X#define ip_addrsize(ipaddrptr) \
X    (((ipaddr_struct *)VARDATA(ipaddrptr))->family == AF_INET ? 4 : -1)
X
X#define ip_family(ipaddrptr) \
X    (((ipaddr_struct *)VARDATA(ipaddrptr))->family)
X
X#define ip_bits(ipaddrptr) \
X    (((ipaddr_struct *)VARDATA(ipaddrptr))->bits)
X
X#define ip_v4addr(ipaddrptr) \
X    (((ipaddr_struct *)VARDATA(ipaddrptr))->addr.ipv4_addr)
X
X/*
X *    Various forward declarations:
X */
X
Xipaddr *ipaddr_in(char *str);
Xchar *ipaddr_out(ipaddr *addr);
X
Xbool ipaddr_lt(ipaddr *a1, ipaddr *a2);
Xbool ipaddr_le(ipaddr *a1, ipaddr *a2);
Xbool ipaddr_eq(ipaddr *a1, ipaddr *a2);
Xbool ipaddr_ge(ipaddr *a1, ipaddr *a2);
Xbool ipaddr_gt(ipaddr *a1, ipaddr *a2);
X
Xbool ipaddr_ne(ipaddr *a1, ipaddr *a2);
X
Xbool ipaddr_sub(ipaddr *a1, ipaddr *a2);
Xbool ipaddr_subeq(ipaddr *a1, ipaddr *a2);
Xbool ipaddr_sup(ipaddr *a1, ipaddr *a2);
Xbool ipaddr_supeq(ipaddr *a1, ipaddr *a2);
X
Xint4 ipaddr_cmp(ipaddr *a1, ipaddr *a2);
X
Xint v4bitncmp(u_int32_t a1, u_int32_t a2, int bits);
X
X/*
X *    IP address reader.
X */
X
Xipaddr *ipaddr_in(char *src) {
X  int bits;
X  ipaddr *dst;
X
X  dst = palloc(VARHDRSZ + sizeof(ipaddr_struct));
X  if (dst == NULL) {
X    elog(ERROR, "unable to allocate memory in ipaddr_in()");
X    return(NULL);
X  }
X  /* First, try for an IP V4 address: */
X  ip_family(dst) = AF_INET;
X  bits = inet_net_pton(ip_family(dst), src, &ip_v4addr(dst), ip_addrsize(dst));
X  if ((bits < 0) || (bits > 32)) {
X    /* Go for an IPV6 address here, before faulting out: */
X    elog(ERROR, "could not parse \"%s\"", src);
X    pfree(dst);
X    return(NULL);
X  }
X  VARSIZE(dst) = VARHDRSZ
X    + ((char *)&ip_v4addr(dst) - (char *)VARDATA(dst))
X    + ip_addrsize(dst);
X  ip_bits(dst) = bits;
X  return(dst);
X}
X
X/*
X *    IP address output function.
X */
X
Xchar *ipaddr_out(ipaddr *src) {
X  char *dst, tmp[sizeof("255.255.255.255/32")];
X
X  if (ip_family(src) == AF_INET) {
X    /* It's an IP V4 address: */
X    if (inet_net_ntop(AF_INET, &ip_v4addr(src), ip_bits(src),
X              tmp, sizeof(tmp)) < 0) {
X      elog(ERROR, "unable to print address (%s)", strerror(errno));
X      return(NULL);
X    }
X  } else {
X    /* Go for an IPV6 address here, before faulting out: */
X    elog(ERROR, "unknown address family (%d)", ip_family(src));
X    return(NULL);
X  }
X  dst = palloc(strlen(tmp) + 1);
X  if (dst == NULL) {
X    elog(ERROR, "unable to allocate memory in ipaddr_out()");
X    return(NULL);
X  }
X  strcpy(dst, tmp);
X  return(dst);
X}
X
X/*
X *    Boolean tests for magnitude.  Add V4/V6 testing!
X */
X
Xbool ipaddr_lt(ipaddr *a1, ipaddr *a2) {
X  if ((ip_family(a1) == AF_INET) && (ip_family(a2) == AF_INET)) {
X    int order = v4bitncmp(ip_v4addr(a1), ip_v4addr(a2), ip_bits(a2));
X    return((order < 0) || ((order == 0) && (ip_bits(a1) < ip_bits(a2))));
X  } else {
X    /* Go for an IPV6 address here, before faulting out: */
X    elog(ERROR, "cannot compare address families %d and %d",
X     ip_family(a1), ip_family(a2));
X    return(FALSE);
X  }
X}
X
Xbool ipaddr_le(ipaddr *a1, ipaddr *a2) {
X  return(ipaddr_lt(a1, a2) || ipaddr_eq(a1, a2));
X}
X
Xbool ipaddr_eq(ipaddr *a1, ipaddr *a2) {
X  if ((ip_family(a1) == AF_INET) && (ip_family(a2) == AF_INET)) {
X    return((ip_bits(a1) == ip_bits(a2))
X       && (v4bitncmp(ip_v4addr(a1), ip_v4addr(a2), ip_bits(a1)) == 0));
X  } else {
X    /* Go for an IPV6 address here, before faulting out: */
X    elog(ERROR, "cannot compare address families %d and %d",
X     ip_family(a1), ip_family(a2));
X    return(FALSE);
X  }
X}
X
Xbool ipaddr_ge(ipaddr *a1, ipaddr *a2) {
X  return(ipaddr_gt(a1, a2) || ipaddr_eq(a1, a2));
X}
X
Xbool ipaddr_gt(ipaddr *a1, ipaddr *a2) {
X  if ((ip_family(a1) == AF_INET) && (ip_family(a2) == AF_INET)) {
X    int order = v4bitncmp(ip_v4addr(a1), ip_v4addr(a2), ip_bits(a2));
X    return((order > 0) || ((order == 0) && (ip_bits(a1) > ip_bits(a2))));
X  } else {
X    /* Go for an IPV6 address here, before faulting out: */
X    elog(ERROR, "cannot compare address families %d and %d",
X     ip_family(a1), ip_family(a2));
X    return(FALSE);
X  }
X}
X
Xbool ipaddr_ne(ipaddr *a1, ipaddr *a2) {
X  return(!ipaddr_eq(a1, a2));
X}
X
Xbool ipaddr_sub(ipaddr *a1, ipaddr *a2) {
X  if ((ip_family(a1) == AF_INET) && (ip_family(a2) == AF_INET)) {
X    return((ip_bits(a1) > ip_bits(a2))
X       && (v4bitncmp(ip_v4addr(a1), ip_v4addr(a2), ip_bits(a2)) == 0));
X  } else {
X    /* Go for an IPV6 address here, before faulting out: */
X    elog(ERROR, "cannot compare address families %d and %d",
X     ip_family(a1), ip_family(a2));
X    return(FALSE);
X  }
X}
X
Xbool ipaddr_subeq(ipaddr *a1, ipaddr *a2) {
X  if ((ip_family(a1) == AF_INET) && (ip_family(a2) == AF_INET)) {
X    return((ip_bits(a1) >= ip_bits(a2))
X       && (v4bitncmp(ip_v4addr(a1), ip_v4addr(a2), ip_bits(a2)) == 0));
X  } else {
X    /* Go for an IPV6 address here, before faulting out: */
X    elog(ERROR, "cannot compare address families %d and %d",
X     ip_family(a1), ip_family(a2));
X    return(FALSE);
X  }
X}
X
Xbool ipaddr_sup(ipaddr *a1, ipaddr *a2) {
X  if ((ip_family(a1) == AF_INET) && (ip_family(a2) == AF_INET)) {
X    return((ip_bits(a1) < ip_bits(a2))
X       && (v4bitncmp(ip_v4addr(a1), ip_v4addr(a2), ip_bits(a1)) == 0));
X  } else {
X    /* Go for an IPV6 address here, before faulting out: */
X    elog(ERROR, "cannot compare address families %d and %d",
X     ip_family(a1), ip_family(a2));
X    return(FALSE);
X  }
X}
X
Xbool ipaddr_supeq(ipaddr *a1, ipaddr *a2) {
X  if ((ip_family(a1) == AF_INET) && (ip_family(a2) == AF_INET)) {
X    return((ip_bits(a1) <= ip_bits(a2))
X       && (v4bitncmp(ip_v4addr(a1), ip_v4addr(a2), ip_bits(a1)) == 0));
X  } else {
X    /* Go for an IPV6 address here, before faulting out: */
X    elog(ERROR, "cannot compare address families %d and %d",
X     ip_family(a1), ip_family(a2));
X    return(FALSE);
X  }
X}
X
X/*
X *    Comparison function for sorting.  Add V4/V6 testing!
X */
X
Xint4 ipaddr_cmp(ipaddr *a1, ipaddr *a2) {
X  if (ntohl(ip_v4addr(a1)) < ntohl(ip_v4addr(a2))) {
X    return(-1);
X  } else if (ntohl(ip_v4addr(a1)) > ntohl(ip_v4addr(a2))) {
X    return(1);
X  }
X  return 0;
X}
X
X/*
X *    Bitwise comparison for V4 addresses.  Add V6 implementation!
X */
X
Xint v4bitncmp(u_int32_t a1, u_int32_t a2, int bits) {
X  unsigned long mask = 0;
X  int i;
X  for (i = 0; i < bits; i++) {
X    mask = (mask >> 1) | 0x80000000;
X  }
X  a1 = ntohl(a1);
X  a2 = ntohl(a2);
X  if ((a1 & mask) < (a2 & mask)) {
X    return(-1);
X  } else if ((a1 & mask) > (a2 & mask)) {
X    return(1);
X  }
X  return(0);
X}
X
X/*
X *    eof
X */
END-of-ip.c
echo x - ip.sql.in
sed 's/^X//' >ip.sql.in << 'END-of-ip.sql.in'
X--
X--    PostgreSQL code for IP addresses.
X--
X--    $Id: ip.sql.in,v 1.2 1998/09/08 12:10:45 tih Exp $
X--
X
Xload 'TARGET/ip.so';
X
X--
X--    Input and output functions and the type itself:
X--
X
Xcreate function ipaddr_in(opaque)
X    returns opaque
X    as 'TARGET/ip.so'
X    language 'c';
X
Xcreate function ipaddr_out(opaque)
X    returns opaque
X    as 'TARGET/ip.so'
X    language 'c';
X
Xcreate type ipaddr (
X    internallength = variable,
X    externallength = variable,
X    input = ipaddr_in,
X    output = ipaddr_out
X);
X
X--
X--    The various boolean tests:
X--
X
Xcreate function ipaddr_lt(ipaddr, ipaddr)
X    returns bool
X    as 'TARGET/ip.so'
X    language 'c';
X
Xcreate function ipaddr_le(ipaddr, ipaddr)
X    returns bool
X    as 'TARGET/ip.so'
X    language 'c';
X
Xcreate function ipaddr_eq(ipaddr, ipaddr)
X    returns bool
X    as 'TARGET/ip.so'
X    language 'c';
X
Xcreate function ipaddr_ge(ipaddr, ipaddr)
X    returns bool
X    as 'TARGET/ip.so'
X    language 'c';
X
Xcreate function ipaddr_gt(ipaddr, ipaddr)
X    returns bool
X    as 'TARGET/ip.so'
X    language 'c';
X
Xcreate function ipaddr_ne(ipaddr, ipaddr)
X    returns bool
X    as 'TARGET/ip.so'
X    language 'c';
X
Xcreate function ipaddr_sub(ipaddr, ipaddr)
X    returns bool
X    as 'TARGET/ip.so'
X    language 'c';
X
Xcreate function ipaddr_subeq(ipaddr, ipaddr)
X    returns bool
X    as 'TARGET/ip.so'
X    language 'c';
X
Xcreate function ipaddr_sup(ipaddr, ipaddr)
X    returns bool
X    as 'TARGET/ip.so'
X    language 'c';
X
Xcreate function ipaddr_supeq(ipaddr, ipaddr)
X    returns bool
X    as 'TARGET/ip.so'
X    language 'c';
X
Xcreate function ipaddr_cmp(ipaddr, ipaddr)
X    returns int4
X    as 'TARGET/ip.so'
X    language 'c';
X
X--
X--    Now the operators.  Note how some of the parameters to some
X--    of the 'create operator' commands are commented out.  This
X--    is because they reference as yet undefined operators, and
X--    will be implicitly defined when those are, further down.
X--
X
Xcreate operator < (
X    leftarg = ipaddr,
X    rightarg = ipaddr,
X--    negator = >=,
X    restrict = intltsel,
X    join = intltjoinsel,
X    procedure = ipaddr_lt
X);
X
Xcreate operator <= (
X    leftarg = ipaddr,
X    rightarg = ipaddr,
X--    negator = >,
X    restrict = intltsel,
X    join = intltjoinsel,
X    procedure = ipaddr_le
X);
X
Xcreate operator = (
X    leftarg = ipaddr,
X    rightarg = ipaddr,
X    commutator = =,
X--    negator = <>,
X    restrict = eqsel,
X    join = eqjoinsel,
X    procedure = ipaddr_eq
X);
X
Xcreate operator >= (
X    leftarg = ipaddr,
X    rightarg = ipaddr,
X    negator = <,
X    restrict = intgtsel,
X    join = intgtjoinsel,
X    procedure = ipaddr_ge
X);
X
Xcreate operator > (
X    leftarg = ipaddr,
X    rightarg = ipaddr,
X    negator = <=,
X    restrict = intgtsel,
X    join = intgtjoinsel,
X    procedure = ipaddr_gt
X);
X
Xcreate operator <> (
X    leftarg = ipaddr,
X    rightarg = ipaddr,
X    negator = =,
X    restrict = neqsel,
X    join = neqjoinsel,
X    procedure = ipaddr_ne
X);
X
Xcreate operator << (
X    leftarg = ipaddr,
X    rightarg = ipaddr,
X    procedure = ipaddr_sub
X);
X
Xcreate operator <<= (
X    leftarg = ipaddr,
X    rightarg = ipaddr,
X    procedure = ipaddr_subeq
X);
X
Xcreate operator >> (
X    leftarg = ipaddr,
X    rightarg = ipaddr,
X    procedure = ipaddr_sup
X);
X
Xcreate operator >>= (
X    leftarg = ipaddr,
X    rightarg = ipaddr,
X    procedure = ipaddr_supeq
X);
X
X--
X--    Finally, make it possible to do btree indexing on IP addresses.
X--
X
Xbegin;
X
Xcreate table tmp_op (oprname name, opi int2);
X
Xinsert into tmp_op values ('<',  1);
Xinsert into tmp_op values ('<=', 2);
Xinsert into tmp_op values ('=',  3);
Xinsert into tmp_op values ('>=', 4);
Xinsert into tmp_op values ('>',  5);
X
Xdelete from pg_opclass
X    where opcname = 'ipaddr_ops';
X
Xinsert into pg_opclass (opcname, opcdeftype)
X    select 'ipaddr_ops', oid from pg_type
X        where typname = 'ipaddr';
X
Xselect o.oid as opoid, o.oprname
X    into table ipaddr_tmp
X    from pg_operator o, pg_type t
X    where o.oprleft = t.oid and
X          o.oprright = t.oid and
X          t.typname = 'ipaddr';
X
Xinsert into pg_amop (amopid, amopclaid, amopopr, amopstrategy,
X             amopselect, amopnpages)
X    select am.oid, opcl.oid, c.opoid, t.opi,
X            'btreesel'::regproc, 'btreenpage'::regproc
X        from pg_am am, pg_opclass opcl, ipaddr_tmp c, tmp_op t
X        where t.oprname = c.oprname and
X              amname = 'btree' and opcname = 'ipaddr_ops';
X
Xinsert into pg_amproc (amid, amopclaid, amproc, amprocnum)
X    select pgam.oid, opc.oid, prc.oid, '1'::int2
X        from pg_am pgam, pg_opclass opc, pg_proc prc
X        where prc.proname = 'ipaddr_cmp' and
X              pgam.amname = 'btree' and
X              opc.opcname = 'ipaddr_ops';
X
Xdrop table tmp_op;
Xdrop table ipaddr_tmp;
X
Xcommit;
X
X--
X--    eof
X--
END-of-ip.sql.in
echo x - mac.c
sed 's/^X//' >mac.c << 'END-of-mac.c'
X/*
X *    PostgreSQL type definitions for MAC addresses.
X *
X *    $Id: mac.c,v 1.1 1998/09/07 12:31:18 tih Exp $
X */
X
X#include <stdio.h>
X
X#include <postgres.h>
X#include <utils/palloc.h>
X
X#include "mac.h"
X
X/*
X *    This is the internal storage format for MAC addresses:
X */
X
Xtypedef struct macaddr {
X  unsigned char a;
X  unsigned char b;
X  unsigned char c;
X  unsigned char d;
X  unsigned char e;
X  unsigned char f;
X} macaddr;
X
X/*
X *    Various forward declarations:
X */
X
Xmacaddr *macaddr_in(char *str);
Xchar *macaddr_out(macaddr *addr);
X
Xbool macaddr_lt(macaddr *a1, macaddr *a2);
Xbool macaddr_le(macaddr *a1, macaddr *a2);
Xbool macaddr_eq(macaddr *a1, macaddr *a2);
Xbool macaddr_ge(macaddr *a1, macaddr *a2);
Xbool macaddr_gt(macaddr *a1, macaddr *a2);
X
Xbool macaddr_ne(macaddr *a1, macaddr *a2);
X
Xint4 macaddr_cmp(macaddr *a1, macaddr *a2);
X
Xtext *macaddr_manuf(macaddr *addr);
X
X/*
X *    Utility macros used for sorting and comparing:
X */
X
X#define hibits(addr) \
X  ((unsigned long)((addr->a<<16)|(addr->b<<8)|(addr->c)))
X
X#define lobits(addr) \
X  ((unsigned long)((addr->c<<16)|(addr->e<<8)|(addr->f)))
X
X/*
X *    MAC address reader.  Accepts several common notations.
X */
X
Xmacaddr *macaddr_in(char *str) {
X  int a, b, c, d, e, f;
X  macaddr *result;
X  int count;
X
X  if (strlen(str) > 0) {
X
X    count = sscanf(str, "%x:%x:%x:%x:%x:%x", &a, &b, &c, &d, &e, &f);
X    if (count != 6)
X      count = sscanf(str, "%x-%x-%x-%x-%x-%x", &a, &b, &c, &d, &e, &f);
X    if (count != 6)
X      count = sscanf(str, "%2x%2x%2x:%2x%2x%2x", &a, &b, &c, &d, &e, &f);
X    if (count != 6)
X      count = sscanf(str, "%2x%2x%2x-%2x%2x%2x", &a, &b, &c, &d, &e, &f);
X    if (count != 6)
X      count = sscanf(str, "%2x%2x.%2x%2x.%2x%2x", &a, &b, &c, &d, &e, &f);
X
X    if (count != 6) {
X      elog(ERROR, "macaddr_in: error in parsing \"%s\"", str);
X      return(NULL);
X    }
X
X    if ((a < 0) || (a > 255) || (b < 0) || (b > 255) ||
X    (c < 0) || (c > 255) || (d < 0) || (d > 255) ||
X    (e < 0) || (e > 255) || (f < 0) || (f > 255)) {
X      elog(ERROR, "macaddr_in: illegal address \"%s\"", str);
X      return(NULL);
X    }
X  } else {
X    a = b = c = d = e = f = 0;    /* special case for missing address */
X  }
X
X  result = (macaddr *)palloc(sizeof(macaddr));
X
X  result->a = a;
X  result->b = b;
X  result->c = c;
X  result->d = d;
X  result->e = e;
X  result->f = f;
X
X  return(result);
X}
X
X/*
X *    MAC address output function.  Fixed format.
X */
X
Xchar *macaddr_out(macaddr *addr) {
X  char *result;
X
X  if (addr == NULL)
X    return(NULL);
X
X  result = (char *)palloc(32);
X
X  if ((hibits(addr) > 0) || (lobits(addr) > 0)) {
X    sprintf(result, "%02x:%02x:%02x:%02x:%02x:%02x",
X        addr->a, addr->b, addr->c, addr->d, addr->e, addr->f);
X  } else {
X    result[0] = 0;        /* special case for missing address */
X  }
X  return(result);
X}
X
X/*
X *    Boolean tests.
X */
X
Xbool macaddr_lt(macaddr *a1, macaddr *a2) {
X  return((hibits(a1) < hibits(a2)) ||
X     ((hibits(a1) == hibits(a2)) && lobits(a1) < lobits(a2)));
X};
X
Xbool macaddr_le(macaddr *a1, macaddr *a2) {
X  return((hibits(a1) < hibits(a2)) ||
X     ((hibits(a1) == hibits(a2)) && lobits(a1) <= lobits(a2)));
X};
X
Xbool macaddr_eq(macaddr *a1, macaddr *a2) {
X  return ((hibits(a1) == hibits(a2)) && (lobits(a1) == lobits(a2)));
X};
X
Xbool macaddr_ge(macaddr *a1, macaddr *a2) {
X  return((hibits(a1) > hibits(a2)) ||
X     ((hibits(a1) == hibits(a2)) && lobits(a1) >= lobits(a2)));
X};
X
Xbool macaddr_gt(macaddr *a1, macaddr *a2) {
X  return((hibits(a1) > hibits(a2)) ||
X     ((hibits(a1) == hibits(a2)) && lobits(a1) > lobits(a2)));
X};
X
Xbool macaddr_ne(macaddr *a1, macaddr *a2) {
X  return ((hibits(a1) != hibits(a2)) || (lobits(a1) != lobits(a2)));
X};
X
X/*
X *    Comparison function for sorting:
X */
X
Xint4 macaddr_cmp(macaddr *a1, macaddr *a2) {
X  if (hibits(a1) < hibits(a2))
X    return -1;
X  else if (hibits(a1) > hibits(a2))
X    return 1;
X  else if (lobits(a1) < lobits(a2))
X    return -1;
X  else if (lobits(a1) > lobits(a2))
X    return 1;
X  else
X    return 0;
X}
X
X/*
X *    The special manufacturer fetching function.  See "mac.h".
X */
X
Xtext *macaddr_manuf(macaddr *addr) {
X  manufacturer *manuf;
X  int length;
X  text *result;
X
X  for (manuf = manufacturers; manuf->name != NULL; manuf++) {
X    if ((manuf->a == addr->a) &&
X    (manuf->b == addr->b) &&
X    (manuf->c == addr->c))
X      break;
X  }
X  if (manuf->name == NULL) {
X    result = palloc(VARHDRSZ + 1);
X    memset(result, 0, VARHDRSZ + 1);
X    VARSIZE(result) = VARHDRSZ + 1;
X  } else {
X    length = strlen(manuf->name) + 1;
X    result = palloc(length + VARHDRSZ);
X    memset(result, 0, length + VARHDRSZ);
X    VARSIZE(result) = length + VARHDRSZ;
X    memcpy(VARDATA(result), manuf->name, length);
X  }
X  return result;
X}
X
X/*
X *    eof
X */
END-of-mac.c
echo x - mac.h
sed 's/^X//' >mac.h << 'END-of-mac.h'
X/*
X *    PostgreSQL type definitions for MAC addresses.
X *
X *    $Id: mac.h,v 1.1 1998/09/07 12:31:22 tih Exp $
X */
X
Xtypedef struct manufacturer {
X  unsigned char a;
X  unsigned char b;
X  unsigned char c;
X  char *name;
X} manufacturer;
X
Xmanufacturer manufacturers[] = {
X  {0x00, 0x00, 0x0C, "Cisco"},
X  {0x00, 0x00, 0x0E, "Fujitsu"},
X  {0x00, 0x00, 0x0F, "NeXT"},
X  {0x00, 0x00, 0x10, "Sytek"},
X  {0x00, 0x00, 0x1D, "Cabletron"},
X  {0x00, 0x00, 0x20, "DIAB"},
X  {0x00, 0x00, 0x22, "Visual Technology"},
X  {0x00, 0x00, 0x2A, "TRW"},
X  {0x00, 0x00, 0x32, "GPT Limited"},
X  {0x00, 0x00, 0x5A, "S & Koch"},
X  {0x00, 0x00, 0x5E, "IANA"},
X  {0x00, 0x00, 0x65, "Network General"},
X  {0x00, 0x00, 0x6B, "MIPS"},
X  {0x00, 0x00, 0x77, "MIPS"},
X  {0x00, 0x00, 0x7A, "Ardent"},
X  {0x00, 0x00, 0x89, "Cayman Systems"},
X  {0x00, 0x00, 0x93, "Proteon"},
X  {0x00, 0x00, 0x9F, "Ameristar Technology"},
X  {0x00, 0x00, 0xA2, "Wellfleet"},
X  {0x00, 0x00, 0xA3, "Network Application Technology"},
X  {0x00, 0x00, 0xA6, "Network General"},
X  {0x00, 0x00, 0xA7, "NCD"},
X  {0x00, 0x00, 0xA9, "Network Systems"},
X  {0x00, 0x00, 0xAA, "Xerox"},
X  {0x00, 0x00, 0xB3, "CIMLinc"},
X  {0x00, 0x00, 0xB7, "Dove Fastnet"},
X  {0x00, 0x00, 0xBC, "Allen-Bradley"},
X  {0x00, 0x00, 0xC0, "Western Digital"},
X  {0x00, 0x00, 0xC5, "Farallon"},
X  {0x00, 0x00, 0xC6, "Hewlett-Packard"},
X  {0x00, 0x00, 0xC8, "Altos"},
X  {0x00, 0x00, 0xC9, "Emulex"},
X  {0x00, 0x00, 0xD7, "Dartmouth College"},
X  {0x00, 0x00, 0xD8, "3Com (?)"},
X  {0x00, 0x00, 0xDD, "Gould"},
X  {0x00, 0x00, 0xDE, "Unigraph"},
X  {0x00, 0x00, 0xE2, "Acer Counterpoint"},
X  {0x00, 0x00, 0xEF, "Alantec"},
X  {0x00, 0x00, 0xFD, "High Level Hardware"},
X  {0x00, 0x01, 0x02, "BBN internal usage"},
X  {0x00, 0x20, 0xAF, "3Com"},
X  {0x00, 0x17, 0x00, "Kabel"},
X  {0x00, 0x80, 0x64, "Wyse Technology"},
X  {0x00, 0x80, 0x2B, "IMAC (?)"},
X  {0x00, 0x80, 0x2D, "Xylogics, Inc."},
X  {0x00, 0x80, 0x8C, "Frontier Software Development"},
X  {0x00, 0x80, 0xC2, "IEEE 802.1 Committee"},
X  {0x00, 0x80, 0xD3, "Shiva"},
X  {0x00, 0xAA, 0x00, "Intel"},
X  {0x00, 0xDD, 0x00, "Ungermann-Bass"},
X  {0x00, 0xDD, 0x01, "Ungermann-Bass"},
X  {0x02, 0x07, 0x01, "Racal InterLan"},
X  {0x02, 0x04, 0x06, "BBN internal usage"},
X  {0x02, 0x60, 0x86, "Satelcom MegaPac"},
X  {0x02, 0x60, 0x8C, "3Com"},
X  {0x02, 0xCF, 0x1F, "CMC"},
X  {0x08, 0x00, 0x02, "3Com"},
X  {0x08, 0x00, 0x03, "ACC"},
X  {0x08, 0x00, 0x05, "Symbolics"},
X  {0x08, 0x00, 0x08, "BBN"},
X  {0x08, 0x00, 0x09, "Hewlett-Packard"},
X  {0x08, 0x00, 0x0A, "Nestar Systems"},
X  {0x08, 0x00, 0x0B, "Unisys"},
X  {0x08, 0x00, 0x11, "Tektronix"},
X  {0x08, 0x00, 0x14, "Excelan"},
X  {0x08, 0x00, 0x17, "NSC"},
X  {0x08, 0x00, 0x1A, "Data General"},
X  {0x08, 0x00, 0x1B, "Data General"},
X  {0x08, 0x00, 0x1E, "Apollo"},
X  {0x08, 0x00, 0x20, "Sun"},
X  {0x08, 0x00, 0x22, "NBI"},
X  {0x08, 0x00, 0x25, "CDC"},
X  {0x08, 0x00, 0x26, "Norsk Data"},
X  {0x08, 0x00, 0x27, "PCS Computer Systems GmbH"},
X  {0x08, 0x00, 0x28, "Texas Instruments"},
X  {0x08, 0x00, 0x2B, "DEC"},
X  {0x08, 0x00, 0x2E, "Metaphor"},
X  {0x08, 0x00, 0x2F, "Prime Computer"},
X  {0x08, 0x00, 0x36, "Intergraph"},
X  {0x08, 0x00, 0x37, "Fujitsu-Xerox"},
X  {0x08, 0x00, 0x38, "Bull"},
X  {0x08, 0x00, 0x39, "Spider Systems"},
X  {0x08, 0x00, 0x41, "DCA Digital Comm. Assoc."},
X  {0x08, 0x00, 0x45, "Xylogics (?)"},
X  {0x08, 0x00, 0x46, "Sony"},
X  {0x08, 0x00, 0x47, "Sequent"},
X  {0x08, 0x00, 0x49, "Univation"},
X  {0x08, 0x00, 0x4C, "Encore"},
X  {0x08, 0x00, 0x4E, "BICC"},
X  {0x08, 0x00, 0x56, "Stanford University"},
X  {0x08, 0x00, 0x58, "DECsystem 20 (?)"},
X  {0x08, 0x00, 0x5A, "IBM"},
X  {0x08, 0x00, 0x67, "Comdesign"},
X  {0x08, 0x00, 0x68, "Ridge"},
X  {0x08, 0x00, 0x69, "Silicon Graphics"},
X  {0x08, 0x00, 0x6E, "Concurrent"},
X  {0x08, 0x00, 0x75, "DDE"},
X  {0x08, 0x00, 0x7C, "Vitalink"},
X  {0x08, 0x00, 0x80, "XIOS"},
X  {0x08, 0x00, 0x86, "Imagen/QMS"},
X  {0x08, 0x00, 0x87, "Xyplex"},
X  {0x08, 0x00, 0x89, "Kinetics"},
X  {0x08, 0x00, 0x8B, "Pyramid"},
X  {0x08, 0x00, 0x8D, "XyVision"},
X  {0x08, 0x00, 0x90, "Retix Inc"},
X  {0x48, 0x44, 0x53, "HDS (?)"},
X  {0x80, 0x00, 0x10, "AT&T"},
X  {0xAA, 0x00, 0x00, "DEC"},
X  {0xAA, 0x00, 0x01, "DEC"},
X  {0xAA, 0x00, 0x02, "DEC"},
X  {0xAA, 0x00, 0x03, "DEC"},
X  {0xAA, 0x00, 0x04, "DEC"},
X  {0x00, 0x00, 0x00, NULL}
X};
X
X/*
X *    eof
X */
END-of-mac.h
echo x - mac.sql.in
sed 's/^X//' >mac.sql.in << 'END-of-mac.sql.in'
X--
X--    PostgreSQL code for MAC addresses.
X--
X--    $Id: mac.sql.in,v 1.2 1998/09/08 12:11:18 tih Exp $
X--
X
Xload 'TARGET/mac.so';
X
X--
X--    Input and output functions and the type itself:
X--
X
Xcreate function macaddr_in(opaque)
X    returns opaque
X    as 'TARGET/mac.so'
X    language 'c';
X
Xcreate function macaddr_out(opaque)
X    returns opaque
X    as 'TARGET/mac.so'
X    language 'c';
X
Xcreate type macaddr (
X    internallength = 6,
X    externallength = variable,
X    input = macaddr_in,
X    output = macaddr_out
X);
X
X--
X--    The boolean tests:
X--
X
Xcreate function macaddr_lt(macaddr, macaddr)
X    returns bool
X    as 'TARGET/mac.so'
X    language 'c';
X
Xcreate function macaddr_le(macaddr, macaddr)
X    returns bool
X    as 'TARGET/mac.so'
X    language 'c';
X
Xcreate function macaddr_eq(macaddr, macaddr)
X    returns bool
X    as 'TARGET/mac.so'
X    language 'c';
X
Xcreate function macaddr_ge(macaddr, macaddr)
X    returns bool
X    as 'TARGET/mac.so'
X    language 'c';
X
Xcreate function macaddr_gt(macaddr, macaddr)
X    returns bool
X    as 'TARGET/mac.so'
X    language 'c';
X
Xcreate function macaddr_ne(macaddr, macaddr)
X    returns bool
X    as 'TARGET/mac.so'
X    language 'c';
X
Xcreate function macaddr_cmp(macaddr, macaddr)
X    returns int4
X    as 'TARGET/mac.so'
X    language 'c';
X
X--
X--    Now the operators.  Note how some of the parameters to some
X--    of the 'create operator' commands are commented out.  This
X--    is because they reference as yet undefined operators, and
X--    will be implicitly defined when those are, further down.
X--
X
Xcreate operator < (
X    leftarg = macaddr,
X    rightarg = macaddr,
X--    negator = >=,
X    procedure = macaddr_lt
X);
X
Xcreate operator <= (
X    leftarg = macaddr,
X    rightarg = macaddr,
X--    negator = >,
X    procedure = macaddr_le
X);
X
Xcreate operator = (
X    leftarg = macaddr,
X    rightarg = macaddr,
X    commutator = =,
X--    negator = <>,
X    procedure = macaddr_eq
X);
X
Xcreate operator >= (
X    leftarg = macaddr,
X    rightarg = macaddr,
X    negator = <,
X    procedure = macaddr_ge
X);
X
Xcreate operator > (
X    leftarg = macaddr,
X    rightarg = macaddr,
X    negator = <=,
X    procedure = macaddr_gt
X);
X
Xcreate operator <> (
X    leftarg = macaddr,
X    rightarg = macaddr,
X    negator = =,
X    procedure = macaddr_ne
X);
X
X--
X--    Finally, the special manufacurer matching function:
X--
X
Xcreate function macaddr_manuf(macaddr)
X    returns text
X    as 'TARGET/mac.so'
X    language 'c';
X
X--
X--    Finally, make it possible to do btree indexing on MAC addresses.
X--
X
Xbegin;
X
Xcreate table tmp_op (oprname name, opi int2);
X
Xinsert into tmp_op values ('<',  1);
Xinsert into tmp_op values ('<=', 2);
Xinsert into tmp_op values ('=',  3);
Xinsert into tmp_op values ('>=', 4);
Xinsert into tmp_op values ('>',  5);
X
Xdelete from pg_opclass
X    where opcname = 'macaddr_ops';
X
Xinsert into pg_opclass (opcname, opcdeftype)
X    select 'macaddr_ops', oid from pg_type
X        where typname = 'macaddr';
X
Xselect o.oid as opoid, o.oprname
X    into table macaddr_tmp
X    from pg_operator o, pg_type t
X    where o.oprleft = t.oid and
X          o.oprright = t.oid and
X          t.typname = 'macaddr';
X
Xinsert into pg_amop (amopid, amopclaid, amopopr, amopstrategy,
X             amopselect, amopnpages)
X    select am.oid, opcl.oid, c.opoid, t.opi,
X            'btreesel'::regproc, 'btreenpage'::regproc
X        from pg_am am, pg_opclass opcl, macaddr_tmp c, tmp_op t
X        where t.oprname = c.oprname and
X              amname = 'btree' and opcname = 'macaddr_ops';
X
Xinsert into pg_amproc (amid, amopclaid, amproc, amprocnum)
X    select pgam.oid, opc.oid, prc.oid, '1'::int2
X        from pg_am pgam, pg_opclass opc, pg_proc prc
X        where prc.proname = 'macaddr_cmp' and
X              pgam.amname = 'btree' and
X              opc.opcname = 'macaddr_ops';
X
Xdrop table tmp_op;
Xdrop table macaddr_tmp;
X
Xcommit;
X
X--
X--    eof
X--
END-of-mac.sql.in
echo x - test.sql
sed 's/^X//' >test.sql << 'END-of-test.sql'
X--
X--    A quick test of the IP address code
X--
X--    $Id: test.sql,v 1.2 1998/09/08 12:11:34 tih Exp $
X--
X
X-- temporary table:
Xcreate table addresses (address ipaddr);
X
X-- sample data from two subnets:
Xinsert into addresses values ('158.37.96.15');
Xinsert into addresses values ('158.37.96.16');
Xinsert into addresses values ('158.37.96.17');
Xinsert into addresses values ('158.37.97.15');
Xinsert into addresses values ('158.37.97.16');
Xinsert into addresses values ('158.37.97.17');
Xinsert into addresses values ('158.37.98.15');
Xinsert into addresses values ('158.37.98.16');
Xinsert into addresses values ('158.37.98.17');
Xinsert into addresses values ('158.37.96.150');
Xinsert into addresses values ('158.37.96.160');
Xinsert into addresses values ('158.37.96.170');
Xinsert into addresses values ('158.37.97.150');
Xinsert into addresses values ('158.37.97.160');
Xinsert into addresses values ('158.37.97.170');
Xinsert into addresses values ('158.37.98.150');
Xinsert into addresses values ('158.37.98.160');
Xinsert into addresses values ('158.37.98.170');
X
X-- show them all:
Xselect * from addresses;
X
X-- select the ones in subnet 96:
Xselect * from addresses where address << '158.37.96.0/24';
X
X-- select the ones not in subnet 96:
Xselect * from addresses where not address << '158.37.96.0/24';
X
X-- select the ones in subnet 97:
Xselect * from addresses where address << '158.37.97.0/24';
X
X-- select the ones not in subnet 97:
Xselect * from addresses where not address << '158.37.97.0/24';
X
X-- select the ones in subnet 96 or 97, sorted:
Xselect * from addresses where address << '158.37.96.0/23'
X    order by address;
X
X-- now some networks:
Xcreate table networks (network ipaddr);
X
X-- now the subnets mentioned above:
Xinsert into networks values ('158.37.96.0/24');
Xinsert into networks values ('158.37.97.0/24');
Xinsert into networks values ('158.37.98.0/24');
X
X-- select matching pairs of addresses and containing nets:
Xselect address, network from addresses, networks
X    where address << network;
X
X-- tidy up:
Xdrop table addresses;
Xdrop table networks;
X
X--
X--    eof
X--
END-of-test.sql
exit

pgsql-hackers by date:

Previous
From: Sferacarta Software
Date:
Subject: Re[2]: [INTERFACES] Re: DELETE FROM TABLE doesn't work (AGAIN)
Next
From: "Thomas G. Lockhart"
Date:
Subject: Re: [HACKERS] Macro From Hell