Thread: ipv6 patch #3

ipv6 patch #3

From
Michael Graff
Date:
This includes several revisions from others.

Major changes:

        Add ipv6 address parsing support to 'inet' and 'cidr' data types.

        Regression tests for IPv6 operations added.

        Documentation updated to document IPv6 bits.

        Stop treating IPv4 as an "unsigned int" and IPv6 as an array of
        characters.  Instead, always use the array of characters so we
        can have one function fits all.  This makes bitncmp(), addressOK(),
        and several other functions "just work" on both address families.

        add family() function which returns integer 4 or 6 for IPv4 or
        IPv6.  (See examples below)  Note that to add this new function
        you will need to dump/initdb/reload or find the correct magic
        to add the function to the postgresql function catalogs.

        IPv4 addresses always sort before IPv6.

        On disk we use AF_INET for IPv4, and AF_INET+1 for IPv6 addresses.
        This prevents the need for a dump and reload, but lets IPv6 parsing
        work on machines without AF_INET6.

        To select all IPv4 addresses from a table:

                select * from foo where family(addr) = 4 ...

        Order by and other bits should all work.

Regression tests pass, at least on my NetBSD machine.

--Michael

diff -ur orig-postgresql-7.3.2/doc/src/sgml/datatype.sgml postgresql-7.3.2/doc/src/sgml/datatype.sgml
--- orig-postgresql-7.3.2/doc/src/sgml/datatype.sgml    2003-01-28 17:09:03.000000000 -0800
+++ postgresql-7.3.2/doc/src/sgml/datatype.sgml    2003-04-22 17:18:52.000000000 -0700
@@ -101,7 +101,7 @@
       <row>
        <entry><type>cidr</type></entry>
        <entry></entry>
-       <entry>IP network address</entry>
+       <entry>IPv4 or IPc6 network address</entry>
       </row>

       <row>
@@ -125,7 +125,7 @@
       <row>
        <entry><type>inet</type></entry>
        <entry></entry>
-       <entry>IP host address</entry>
+       <entry>IPv4 or IPv6 host address</entry>
       </row>

       <row>
@@ -2538,7 +2538,7 @@
    </indexterm>

    <para>
-    <productname>PostgreSQL</> offers data types to store IP and MAC
+    <productname>PostgreSQL</> offers data types to store IPv4, IPv6, and MAC
     addresses, shown in <xref linkend="datatype-net-types-table">.  It
     is preferable to use these types over plain text types, because
     these types offer input error checking and several specialized
@@ -2560,16 +2560,16 @@

        <row>
     <entry><type>cidr</type></entry>
-    <entry>12 bytes</entry>
+    <entry>12 or 24 bytes</entry>
     <entry>IP networks</entry>
-    <entry>valid IPv4 networks</entry>
+    <entry>valid IPv4 or IPv6 networks</entry>
        </row>

        <row>
     <entry><type>inet</type></entry>
-    <entry>12 bytes</entry>
+    <entry>12 or 24 bytes</entry>
     <entry>IP hosts and networks</entry>
-    <entry>valid IPv4 hosts or networks</entry>
+    <entry>valid IPv4 or IPv6 hosts or networks</entry>
        </row>

        <row>
@@ -2584,7 +2584,10 @@
     </table>

    <para>
-    IPv6 is not yet supported.
+    When sorting <type>inet</type> or <type>cidr</type> data types,
+    IPv4 addresses will always sort before IPv6 addresses, including
+    IPv4 addresses encapsulated or mapped into IPv6 addresses, such as
+    ::10.2.3.4 or ::ffff::10.4.3.2.
    </para>


@@ -2596,26 +2599,31 @@
     </indexterm>

     <para>
-     The <type>inet</type> type holds an IP host address, and
+     The <type>inet</type> type holds an IPv4 or IPv6 host address, and
      optionally the identity of the subnet it is in, all in one field.
      The subnet identity is represented by the number of bits in the
      network part of the address (the <quote>netmask</quote>).  If the
-     netmask is 32,
-     then the value does not indicate a subnet, only a single host.
+     netmask is 32 and the address is IPv4, then the value does not
+     indicate a subnet, only a single host.  In IPv6, the address
+     length is 128 bits.
      Note that if you want to accept networks only, you should use the
      <type>cidr</type> type rather than <type>inet</type>.
     </para>

     <para>
-     The input format for this type is <replaceable
-     class="parameter">x.x.x.x/y</replaceable> where <replaceable
-     class="parameter">x.x.x.x</replaceable> is an IP address and
-     <replaceable class="parameter">y</replaceable> is the number of
-     bits in the netmask.  If the <replaceable
-     class="parameter">/y</replaceable> part is left off, then the
-     netmask is 32, and the value represents just a single host.
-     On display, the <replaceable class="parameter">/y</replaceable>
-     portion is suppressed if the netmask is 32.
+     The input format for this type is
+     <replaceable class="parameter">address/y</replaceable>
+     where
+     <replaceable class="parameter">address</replaceable>
+     is an IPv4 or IPv6 address and
+     <replaceable class="parameter">y</replaceable>
+     is the number of bits in the netmask.  If the
+     <replaceable class="parameter">/y</replaceable>
+     part is left off, then the
+     netmask is 32 for IPv4 and 128 for IPv6, and the value represents
+     just a single host.  On display, the
+     <replaceable class="parameter">/y</replaceable>
+     portion is suppressed if the netmask specifies a single host.
     </para>
    </sect2>

@@ -2627,13 +2635,14 @@
     </indexterm>

     <para>
-     The <type>cidr</type> type holds an IP network specification.
+     The <type>cidr</type> type holds an IPv4 or IPv6 network specification.
      Input and output formats follow Classless Internet Domain Routing
      conventions.
      The format for
      specifying classless networks is <replaceable
-     class="parameter">x.x.x.x/y</> where <replaceable
-     class="parameter">x.x.x.x</> is the network and <replaceable
+     class="parameter">address/y</> where <replaceable
+     class="parameter">address</> is the network represented as an
+     IPv4 or IPv6 address, and <replaceable
      class="parameter">y</> is the number of bits in the netmask.  If
      <replaceable class="parameter">y</> is omitted, it is calculated
      using assumptions from the older classful numbering system, except
@@ -2711,6 +2720,28 @@
      <entry>10.0.0.0/8</entry>
      <entry>10/8</entry>
     </row>
+    <row>
+     <entry>10.1.2.3/32</entry>
+     <entry>10.1.2.3/32</entry>
+         <entry>10.1.2.3/32</entry>
+        <row>
+     <entry>2001:4f8:3:ba::/64</entry>
+     <entry>2001:4f8:3:ba::/64</entry>
+     <entry>2001:4f8:3:ba::/64</entry>
+    </row>
+        <row>
+     <entry>2001:4f8:3:ba:2e0:81ff:fe22:d1f1/128</entry>
+     <entry>2001:4f8:3:ba:2e0:81ff:fe22:d1f1/128</entry>
+     <entry>2001:4f8:3:ba:2e0:81ff:fe22:d1f1</entry>
+    </row>
+    <row>
+     <entry>::ffff:1.2.3.0/120</entry>
+     <entry>::ffff:1.2.3.0/120</entry>
+         <entry>::ffff:1.2.3/120</entry>
+    <row>
+     <entry>::ffff:1.2.3.0/128</entry>
+     <entry>::ffff:1.2.3.0/128</entry>
+         <entry>::ffff:1.2.3.0/128</entry>
        </tbody>
       </tgroup>
      </table>
diff -ur orig-postgresql-7.3.2/doc/src/sgml/func.sgml postgresql-7.3.2/doc/src/sgml/func.sgml
--- orig-postgresql-7.3.2/doc/src/sgml/func.sgml    2003-01-22 17:23:14.000000000 -0800
+++ postgresql-7.3.2/doc/src/sgml/func.sgml    2003-04-22 17:20:23.000000000 -0700
@@ -4886,6 +4886,11 @@
     <entry><literal>inet '192.168.1.5' < inet '192.168.1.6'</literal></entry>
        </row>
        <row>
+    <entry> < </entry>
+    <entry>Less than</entry>
+    <entry><literal>inet '1111::2222' < inet '2222::1111'</literal></entry>
+       </row>
+       <row>
     <entry> <= </entry>
     <entry>Less than or equal</entry>
     <entry><literal>inet '192.168.1.5' <= inet '192.168.1.5'</literal></entry>
diff -ur orig-postgresql-7.3.2/src/backend/utils/adt/inet_net_ntop.c
postgresql-7.3.2/src/backend/utils/adt/inet_net_ntop.c
--- orig-postgresql-7.3.2/src/backend/utils/adt/inet_net_ntop.c    2002-09-01 19:47:04.000000000 -0700
+++ postgresql-7.3.2/src/backend/utils/adt/inet_net_ntop.c    2003-04-22 17:12:05.000000000 -0700
@@ -27,8 +27,12 @@

 #include <errno.h>

+#include "utils/inet.h"
 #include "utils/builtins.h"

+#define NS_IN6ADDRSZ 16
+#define NS_INT16SZ 2
+
 #ifdef SPRINTF_CHAR
 #define SPRINTF(x) strlen(sprintf/**/x)
 #else
@@ -36,9 +40,13 @@
 #endif

 static char *inet_net_ntop_ipv4(const u_char *src, int bits,
-                   char *dst, size_t size);
+                char *dst, size_t size);
 static char *inet_cidr_ntop_ipv4(const u_char *src, int bits,
-                    char *dst, size_t size);
+                 char *dst, size_t size);
+static char *inet_net_ntop_ipv6(const u_char *src, int bits,
+                char *dst, size_t size);
+static char *inet_cidr_ntop_ipv6(const u_char *src, int bits,
+                 char *dst, size_t size);

 /*
  * char *
@@ -55,8 +63,10 @@
 {
     switch (af)
     {
-        case AF_INET:
+        case PGSQL_AF_INET:
             return (inet_cidr_ntop_ipv4(src, bits, dst, size));
+        case PGSQL_AF_INET6:
+            return (inet_cidr_ntop_ipv6(src, bits, dst, size));
         default:
             errno = EAFNOSUPPORT;
             return (NULL);
@@ -136,6 +146,148 @@
     return (NULL);
 }

+/*
+ * static char *
+ * inet_net_ntop_ipv6(src, bits, fakebits, dst, size)
+ *    convert IPv6 network number from network to presentation format.
+ *    generates CIDR style result always. Picks the shortest representation
+ *    unless the IP is really IPv4.
+ *    always prints specified number of bits (bits).
+ * return:
+ *    pointer to dst, or NULL if an error occurred (check errno).
+ * note:
+ *    network byte order assumed.  this means 192.5.5.240/28 has
+ *    0x11110000 in its fourth octet.
+ * author:
+ *    Vadim Kogan (UCB), June 2001
+ *  Original version (IPv4) by Paul Vixie (ISC), July 1996
+ */
+
+static char *
+inet_cidr_ntop_ipv6(const u_char *src, int bits, char *dst, size_t size)
+{
+    u_int    m;
+    int    b;
+    int    p;
+    int    zero_s, zero_l, tmp_zero_s, tmp_zero_l;
+    int    i;
+    int    is_ipv4 = 0;
+    int     double_colon = 0;
+    unsigned char inbuf[16];
+    char outbuf[sizeof("xxxx:xxxx:xxxx:xxxx:xxxx:xxxx:255.255.255.255/128")];
+    char    *cp;
+    int    words;
+    u_char    *s;
+
+    if (bits < 0 || bits > 128) {
+        errno = EINVAL;
+        return (NULL);
+    }
+
+    cp = outbuf;
+    double_colon = 0;
+
+    if (bits == 0) {
+        *cp++ = ':';
+        *cp++ = ':';
+        *cp = '\0';
+        double_colon = 1;
+    } else {
+        /* Copy src to private buffer.  Zero host part. */
+        p = (bits + 7) / 8;
+        memcpy(inbuf, src, p);
+        memset(inbuf + p, 0, 16 - p);
+        b = bits % 8;
+        if (b != 0) {
+            m = ~0 << (8 - b);
+            inbuf[p-1] &= m;
+        }
+
+        s = inbuf;
+
+        /* how many words need to be displayed in output */
+        words = (bits + 15) / 16;
+        if (words == 1)
+            words = 2;
+
+        /* Find the longest substring of zero's */
+        zero_s = zero_l = tmp_zero_s = tmp_zero_l = 0;
+        for (i = 0; i < (words * 2); i += 2) {
+            if ((s[i] | s[i+1]) == 0) {
+                if (tmp_zero_l == 0)
+                    tmp_zero_s = i / 2;
+                tmp_zero_l++;
+            } else {
+                if (tmp_zero_l && zero_l < tmp_zero_l) {
+                    zero_s = tmp_zero_s;
+                    zero_l = tmp_zero_l;
+                    tmp_zero_l = 0;
+                }
+            }
+        }
+
+        if (tmp_zero_l && zero_l < tmp_zero_l) {
+            zero_s = tmp_zero_s;
+            zero_l = tmp_zero_l;
+        }
+
+        if (zero_l != words && zero_s == 0 && ((zero_l == 6) ||
+            ((zero_l == 5 && s[10] == 0xff && s[11] == 0xff) ||
+            ((zero_l == 7 && s[14] != 0 && s[15] != 1)))))
+            is_ipv4 = 1;
+
+        /* Format whole words. */
+        for (p = 0; p < words; p++) {
+            if (zero_l != 0 && p >= zero_s && p < zero_s + zero_l) {
+                /* Time to skip some zeros */
+                if (p == zero_s)
+                    *cp++ = ':';
+                if (p == words - 1) {
+                    *cp++ = ':';
+                    double_colon = 1;
+                }
+                s++;
+                s++;
+                continue;
+            }
+
+            if (is_ipv4 && p > 5 ) {
+                *cp++ = (p == 6) ? ':' : '.';
+                cp += SPRINTF((cp, "%u", *s++));
+                /* we can potentially drop the last octet */
+                if (p != 7 || bits > 120) {
+                    *cp++ = '.';
+                    cp += SPRINTF((cp, "%u", *s++));
+                }
+            } else {
+                if (cp != outbuf)
+                    *cp++ = ':';
+                cp += SPRINTF((cp, "%x", *s * 256 + s[1]));
+                s += 2;
+            }
+        }
+    }
+
+    if (!double_colon) {
+        if (bits < 128 - 32)
+            cp += SPRINTF((cp, "::", bits));
+        else if (bits < 128 - 16)
+            cp += SPRINTF((cp, ":0", bits));
+    }
+
+    /* Format CIDR /width. */
+    SPRINTF((cp, "/%u", bits));
+
+    if (strlen(outbuf) + 1 > size)
+        goto emsgsize;
+    strcpy(dst, outbuf);
+
+    return (dst);
+
+emsgsize:
+    errno = EMSGSIZE;
+    return (NULL);
+}

 /*
  * char *
@@ -156,8 +308,10 @@
 {
     switch (af)
     {
-        case AF_INET:
+        case PGSQL_AF_INET:
             return (inet_net_ntop_ipv4(src, bits, dst, size));
+        case PGSQL_AF_INET6:
+            return (inet_net_ntop_ipv6(src, bits, dst, size));
         default:
             errno = EAFNOSUPPORT;
             return (NULL);
@@ -217,3 +371,127 @@
     errno = EMSGSIZE;
     return (NULL);
 }
+
+static int
+decoct(const u_char *src, int bytes, char *dst, size_t size) {
+    char *odst = dst;
+    char *t;
+    int b;
+
+    for (b = 1; b <= bytes; b++) {
+        if (size < sizeof "255.")
+            return (0);
+        t = dst;
+        dst += SPRINTF((dst, "%u", *src++));
+        if (b != bytes) {
+            *dst++ = '.';
+            *dst = '\0';
+        }
+        size -= (size_t)(dst - t);
+    }
+    return (dst - odst);
+}
+
+static char *
+inet_net_ntop_ipv6(const u_char *src, int bits, char *dst, size_t size)
+{
+    /*
+     * Note that int32_t and int16_t need only be "at least" large enough
+     * to contain a value of the specified size.  On some systems, like
+     * Crays, there is no such thing as an integer variable with 16 bits.
+     * Keep this in mind if you think this function should have been coded
+     * to use pointer overlays.  All the world's not a VAX.
+     */
+    char tmp[sizeof "ffff:ffff:ffff:ffff:ffff:ffff:255.255.255.255/128"];
+    char *tp;
+    struct { int base, len; } best, cur;
+    u_int words[NS_IN6ADDRSZ / NS_INT16SZ];
+    int i;
+
+    if ((bits < -1) || (bits > 128)) {
+        errno = EINVAL;
+        return (NULL);
+    }
+
+    /*
+     * Preprocess:
+     *    Copy the input (bytewise) array into a wordwise array.
+     *    Find the longest run of 0x00's in src[] for :: shorthanding.
+     */
+    memset(words, '\0', sizeof words);
+    for (i = 0; i < NS_IN6ADDRSZ; i++)
+        words[i / 2] |= (src[i] << ((1 - (i % 2)) << 3));
+    best.base = -1;
+    cur.base = -1;
+    for (i = 0; i < (NS_IN6ADDRSZ / NS_INT16SZ); i++) {
+        if (words[i] == 0) {
+            if (cur.base == -1)
+                cur.base = i, cur.len = 1;
+            else
+                cur.len++;
+        } else {
+            if (cur.base != -1) {
+                if (best.base == -1 || cur.len > best.len)
+                    best = cur;
+                cur.base = -1;
+            }
+        }
+    }
+    if (cur.base != -1) {
+        if (best.base == -1 || cur.len > best.len)
+            best = cur;
+    }
+    if (best.base != -1 && best.len < 2)
+        best.base = -1;
+
+    /*
+     * Format the result.
+     */
+    tp = tmp;
+    for (i = 0; i < (NS_IN6ADDRSZ / NS_INT16SZ); i++) {
+        /* Are we inside the best run of 0x00's? */
+        if (best.base != -1 && i >= best.base &&
+            i < (best.base + best.len)) {
+            if (i == best.base)
+                *tp++ = ':';
+            continue;
+        }
+        /* Are we following an initial run of 0x00s or any real hex? */
+        if (i != 0)
+            *tp++ = ':';
+        /* Is this address an encapsulated IPv4? */
+        if (i == 6 && best.base == 0 && (best.len == 6 ||
+            (best.len == 7 && words[7] != 0x0001) ||
+            (best.len == 5 && words[5] == 0xffff))) {
+            int n;
+
+            n = decoct(src+12, 4, tp, sizeof tmp - (tp - tmp));
+            if (n == 0) {
+                errno = EMSGSIZE;
+                return (NULL);
+            }
+            tp += strlen(tp);
+            break;
+        }
+        tp += SPRINTF((tp, "%x", words[i]));
+    }
+
+    /* Was it a trailing run of 0x00's? */
+    if (best.base != -1 && (best.base + best.len) ==
+        (NS_IN6ADDRSZ / NS_INT16SZ))
+        *tp++ = ':';
+    *tp = '\0';
+
+    if (bits != -1 && bits != 128)
+        tp += SPRINTF((tp, "/%u", bits));
+
+    /*
+     * Check for overflow, copy, and we're done.
+     */
+    if ((size_t)(tp - tmp) > size) {
+        errno = EMSGSIZE;
+        return (NULL);
+    }
+    strcpy(dst, tmp);
+    return (dst);
+}
diff -ur orig-postgresql-7.3.2/src/backend/utils/adt/inet_net_pton.c
postgresql-7.3.2/src/backend/utils/adt/inet_net_pton.c
--- orig-postgresql-7.3.2/src/backend/utils/adt/inet_net_pton.c    2002-09-01 19:47:04.000000000 -0700
+++ postgresql-7.3.2/src/backend/utils/adt/inet_net_pton.c    2003-04-22 13:02:07.000000000 -0700
@@ -29,16 +29,14 @@
 #include <ctype.h>
 #include <errno.h>

-#include "utils/builtins.h"
+#include "utils/inet.h"

-#ifdef SPRINTF_CHAR
-#define SPRINTF(x) strlen(sprintf/**/x)
-#else
-#define SPRINTF(x) ((size_t)sprintf x)
-#endif
+#include "utils/builtins.h"

 static int    inet_net_pton_ipv4(const char *src, u_char *dst);
 static int    inet_cidr_pton_ipv4(const char *src, u_char *dst, size_t size);
+static int      inet_net_pton_ipv6(const char *src, u_char *dst);
+static int      inet_cidr_pton_ipv6(const char *src, u_char *dst, size_t size);

 /*
  * static int
@@ -63,10 +61,14 @@
 {
     switch (af)
     {
-        case AF_INET:
+        case PGSQL_AF_INET:
             return size == -1 ?
                 inet_net_pton_ipv4(src, dst) :
                 inet_cidr_pton_ipv4(src, dst, size);
+        case PGSQL_AF_INET6:
+            return size == -1 ?
+                inet_net_pton_ipv6(src, dst) :
+                inet_cidr_pton_ipv6(src, dst, size);
         default:
             errno = EAFNOSUPPORT;
             return (-1);
@@ -335,3 +337,199 @@
     errno = EMSGSIZE;
     return (-1);
 }
+
+static int
+getbits(const char *src, int *bitsp) {
+    static const char digits[] = "0123456789";
+    int n;
+    int val;
+    char ch;
+
+    val = 0;
+    n = 0;
+    while ((ch = *src++) != '\0') {
+        const char *pch;
+
+        pch = strchr(digits, ch);
+        if (pch != NULL) {
+            if (n++ != 0 && val == 0)    /* no leading zeros */
+                return (0);
+            val *= 10;
+            val += (pch - digits);
+            if (val > 128)            /* range */
+                return (0);
+            continue;
+        }
+        return (0);
+    }
+    if (n == 0)
+        return (0);
+    *bitsp = val;
+    return (1);
+}
+
+static int
+getv4(const char *src, u_char *dst, int *bitsp) {
+    static const char digits[] = "0123456789";
+    u_char *odst = dst;
+    int n;
+    u_int val;
+    char ch;
+
+    val = 0;
+    n = 0;
+    while ((ch = *src++) != '\0') {
+        const char *pch;
+
+        pch = strchr(digits, ch);
+        if (pch != NULL) {
+            if (n++ != 0 && val == 0)    /* no leading zeros */
+                return (0);
+            val *= 10;
+            val += (pch - digits);
+            if (val > 255)            /* range */
+                return (0);
+            continue;
+        }
+        if (ch == '.' || ch == '/') {
+            if (dst - odst > 3)        /* too many octets? */
+                return (0);
+            *dst++ = val;
+            if (ch == '/')
+                return (getbits(src, bitsp));
+            val = 0;
+            n = 0;
+            continue;
+        }
+        return (0);
+    }
+    if (n == 0)
+        return (0);
+    if (dst - odst > 3)        /* too many octets? */
+        return (0);
+    *dst++ = val;
+    return (1);
+}
+
+static int
+inet_net_pton_ipv6(const char *src, u_char *dst)
+{
+    return inet_cidr_pton_ipv6(src, dst, 16);
+}
+
+#define NS_IN6ADDRSZ 16
+#define NS_INT16SZ 2
+#define NS_INADDRSZ 4
+
+static int
+inet_cidr_pton_ipv6(const char *src, u_char *dst, size_t size) {
+    static const char xdigits_l[] = "0123456789abcdef",
+              xdigits_u[] = "0123456789ABCDEF";
+    u_char tmp[NS_IN6ADDRSZ], *tp, *endp, *colonp;
+    const char *xdigits, *curtok;
+    int ch, saw_xdigit;
+    u_int val;
+    int digits;
+    int bits;
+
+    if (size < NS_IN6ADDRSZ)
+        goto emsgsize;
+
+    memset((tp = tmp), '\0', NS_IN6ADDRSZ);
+    endp = tp + NS_IN6ADDRSZ;
+    colonp = NULL;
+    /* Leading :: requires some special handling. */
+    if (*src == ':')
+        if (*++src != ':')
+            goto enoent;
+    curtok = src;
+    saw_xdigit = 0;
+    val = 0;
+    digits = 0;
+    bits = -1;
+    while ((ch = *src++) != '\0') {
+        const char *pch;
+
+        if ((pch = strchr((xdigits = xdigits_l), ch)) == NULL)
+            pch = strchr((xdigits = xdigits_u), ch);
+        if (pch != NULL) {
+            val <<= 4;
+            val |= (pch - xdigits);
+            if (++digits > 4)
+                goto enoent;
+            saw_xdigit = 1;
+            continue;
+        }
+        if (ch == ':') {
+            curtok = src;
+            if (!saw_xdigit) {
+                if (colonp)
+                    goto enoent;
+                colonp = tp;
+                continue;
+            } else if (*src == '\0')
+                goto enoent;
+            if (tp + NS_INT16SZ > endp)
+                return (0);
+            *tp++ = (u_char) (val >> 8) & 0xff;
+            *tp++ = (u_char) val & 0xff;
+            saw_xdigit = 0;
+            digits = 0;
+            val = 0;
+            continue;
+        }
+        if (ch == '.' && ((tp + NS_INADDRSZ) <= endp) &&
+             getv4(curtok, tp, &bits) > 0) {
+            tp += NS_INADDRSZ;
+            saw_xdigit = 0;
+            break;    /* '\0' was seen by inet_pton4(). */
+        }
+        if (ch == '/' && getbits(src, &bits) > 0)
+            break;
+        goto enoent;
+    }
+    if (saw_xdigit) {
+        if (tp + NS_INT16SZ > endp)
+            goto enoent;
+        *tp++ = (u_char) (val >> 8) & 0xff;
+        *tp++ = (u_char) val & 0xff;
+    }
+    if (bits == -1)
+        bits = 128;
+
+    endp =  tmp + 16;
+
+    if (colonp != NULL) {
+        /*
+         * Since some memmove()'s erroneously fail to handle
+         * overlapping regions, we'll do the shift by hand.
+         */
+        const int n = tp - colonp;
+        int i;
+
+        if (tp == endp)
+            goto enoent;
+        for (i = 1; i <= n; i++) {
+            endp[- i] = colonp[n - i];
+            colonp[n - i] = 0;
+        }
+        tp = endp;
+    }
+    if (tp != endp)
+        goto enoent;
+
+    /*
+     * Copy out the result.
+     */
+    memcpy(dst, tmp, NS_IN6ADDRSZ);
+
+    return (bits);
+
+ enoent:
+    errno = ENOENT;
+    return (-1);
+
+ emsgsize:
+    errno = EMSGSIZE;
+    return (-1);
+}
diff -ur orig-postgresql-7.3.2/src/backend/utils/adt/network.c postgresql-7.3.2/src/backend/utils/adt/network.c
--- orig-postgresql-7.3.2/src/backend/utils/adt/network.c    2002-09-01 19:47:04.000000000 -0700
+++ postgresql-7.3.2/src/backend/utils/adt/network.c    2003-05-20 16:12:33.000000000 -0700
@@ -1,7 +1,5 @@
 /*
- *    PostgreSQL type definitions for the INET type.    This
- *    is for IP V4 CIDR notation, but prepared for V6: just
- *    add the necessary bits where the comments indicate.
+ *    PostgreSQL type definitions for the INET and CIDR types.
  *
  *    $Header: /cvsroot/pgsql-server/src/backend/utils/adt/network.c,v 1.35 2002/09/02 02:47:04 momjian Exp $
  *
@@ -15,23 +13,22 @@
 #include <netinet/in.h>
 #include <arpa/inet.h>

+#include <assert.h>
+
 #include "catalog/pg_type.h"
 #include "utils/builtins.h"
 #include "utils/inet.h"

-
 static Datum text_network(text *src, int type);
 static int32 network_cmp_internal(inet *a1, inet *a2);
-static int    v4bitncmp(unsigned long a1, unsigned long a2, int bits);
-static bool v4addressOK(unsigned long a1, int bits);
+static int bitncmp(void *l, void *r, int n);
+static bool addressOK(unsigned char *a, int bits, int family);
+static int ip_addrsize(inet *inetptr);

 /*
- *    Access macros.    Add IPV6 support.
+ *    Access macros.
  */

-#define ip_addrsize(inetptr) \
-    (((inet_struct *)VARDATA(inetptr))->family == AF_INET ? 4 : -1)
-
 #define ip_family(inetptr) \
     (((inet_struct *)VARDATA(inetptr))->family)

@@ -41,43 +38,70 @@
 #define ip_type(inetptr) \
     (((inet_struct *)VARDATA(inetptr))->type)

-#define ip_v4addr(inetptr) \
-    (((inet_struct *)VARDATA(inetptr))->addr.ipv4_addr)
+#define ip_addr(inetptr) \
+    (((inet_struct *)VARDATA(inetptr))->ip_addr)
+
+#define ip_maxbits(inetptr) \
+    (ip_family(inetptr) == PGSQL_AF_INET ? 32 : 128)
+
+/*
+ * Now, as a function!
+ * Return the number of bytes of storage needed for this data type.
+ */
+static int
+ip_addrsize(inet *inetptr)
+{
+    switch (ip_family(inetptr)) {
+    case PGSQL_AF_INET:
+        return 4;
+    case PGSQL_AF_INET6:
+        return 16;
+    default:
+        return -1;
+    }
+}

 /* Common input routine */
 static inet *
 network_in(char *src, int type)
 {
-    int            bits;
+    int        bits;
     inet       *dst;

     dst = (inet *) palloc(VARHDRSZ + sizeof(inet_struct));
     /* make sure any unused bits in a CIDR value are zeroed */
     MemSet(dst, 0, VARHDRSZ + sizeof(inet_struct));

-    /* First, try for an IP V4 address: */
-    ip_family(dst) = AF_INET;
-    bits = inet_net_pton(ip_family(dst), src, &ip_v4addr(dst),
-                         type ? ip_addrsize(dst) : -1);
-    if ((bits < 0) || (bits > 32))
-    {
-        /* Go for an IPV6 address here, before faulting out: */
-        elog(ERROR, "invalid %s value '%s'",
-             type ? "CIDR" : "INET", src);
+    /*
+     * First, check to see if this is an IPv6 or IPv4 address.  IPv6
+     * addresses will have a : somewhere in them (several, in fact) so
+     * if there is one present, assume it's V6, otherwise assume it's V4.
+     */
+
+    if (strchr(src, ':') != NULL) {
+        ip_family(dst) = PGSQL_AF_INET6;
+    } else {
+        ip_family(dst) = PGSQL_AF_INET;
     }

+    bits = inet_net_pton(ip_family(dst), src, ip_addr(dst),
+                 type ? ip_addrsize(dst) : -1);
+    if ((bits < 0) || (bits > ip_maxbits(dst)))
+        elog(ERROR, "invalid %s value '%s'",
+             type ? "CIDR" : "INET", src);
+
     /*
-     * Error check: CIDR values must not have any bits set beyond the
-     * masklen. XXX this code is not IPV6 ready.
+     * Error check: CIDR values must not have any bits set beyond
+     * the masklen.
      */
     if (type)
     {
-        if (!v4addressOK(ip_v4addr(dst), bits))
+        if (!addressOK(ip_addr(dst), bits, ip_family(dst)))
             elog(ERROR, "invalid CIDR value '%s': has bits set to right of mask", src);
     }

     VARATT_SIZEP(dst) = VARHDRSZ
-        + ((char *) &ip_v4addr(dst) - (char *) VARDATA(dst))
+        + ((char *) &ip_addr(dst) - (char *) VARDATA(dst))
         + ip_addrsize(dst);
     ip_bits(dst) = bits;
     ip_type(dst) = type;
@@ -110,32 +134,20 @@
 inet_out(PG_FUNCTION_ARGS)
 {
     inet       *src = PG_GETARG_INET_P(0);
-    char        tmp[sizeof("255.255.255.255/32")];
+    char        tmp[sizeof("xxxx:xxxx:xxxx:xxxx:xxxx:xxxx:255.255.255.255/128")];
     char       *dst;
     int            len;

-    if (ip_family(src) == AF_INET)
+    dst = inet_net_ntop(ip_family(src), ip_addr(src), ip_bits(src),
+                tmp, sizeof(tmp));
+    if (dst == NULL)
+        elog(ERROR, "unable to print address (%s)", strerror(errno));
+    /* For CIDR, add /n if not present */
+    if (ip_type(src) && strchr(tmp, '/') == NULL)
     {
-        /* It's an IP V4 address: */
-
-        /*
-         * Use inet style for both inet and cidr, since we don't want
-         * abbreviated CIDR style here.
-         */
-        dst = inet_net_ntop(AF_INET, &ip_v4addr(src), ip_bits(src),
-                            tmp, sizeof(tmp));
-        if (dst == NULL)
-            elog(ERROR, "unable to print address (%s)", strerror(errno));
-        /* For CIDR, add /n if not present */
-        if (ip_type(src) && strchr(tmp, '/') == NULL)
-        {
-            len = strlen(tmp);
-            snprintf(tmp + len, sizeof(tmp) - len, "/%u", ip_bits(src));
-        }
+        len = strlen(tmp);
+        snprintf(tmp + len, sizeof(tmp) - len, "/%u", ip_bits(src));
     }
-    else
-        /* Go for an IPV6 address here, before faulting out: */
-        elog(ERROR, "unknown address family (%d)", ip_family(src));

     PG_RETURN_CSTRING(pstrdup(tmp));
 }
@@ -162,6 +174,7 @@
     PG_RETURN_INET_P(network_in(str, type));
 }

+
 Datum
 text_cidr(PG_FUNCTION_ARGS)
 {
@@ -181,8 +194,11 @@
     int            bits = PG_GETARG_INT32(1);
     inet       *dst;

-    if ((bits < 0) || (bits > 32))        /* no support for v6 yet */
-        elog(ERROR, "set_masklen - invalid value '%d'", bits);
+        if ( bits == -1 )
+            bits = ip_maxbits(src);
+
+    if ((bits < 0) || (bits > ip_maxbits(src)))
+            elog(ERROR, "set_masklen - invalid value '%d'", bits);

     /* clone the original data */
     dst = (inet *) palloc(VARHDRSZ + sizeof(inet_struct));
@@ -207,26 +223,21 @@
 static int32
 network_cmp_internal(inet *a1, inet *a2)
 {
-    if (ip_family(a1) == AF_INET && ip_family(a2) == AF_INET)
+    if (ip_family(a1) == ip_family(a2))
     {
         int            order;

-        order = v4bitncmp(ip_v4addr(a1), ip_v4addr(a2),
-                          Min(ip_bits(a1), ip_bits(a2)));
+        order = bitncmp(ip_addr(a1), ip_addr(a2),
+                Min(ip_bits(a1), ip_bits(a2)));
         if (order != 0)
             return order;
         order = ((int) ip_bits(a1)) - ((int) ip_bits(a2));
         if (order != 0)
             return order;
-        return v4bitncmp(ip_v4addr(a1), ip_v4addr(a2), 32);
-    }
-    else
-    {
-        /* Go for an IPV6 address here, before faulting out: */
-        elog(ERROR, "cannot compare address families %d and %d",
-             ip_family(a1), ip_family(a2));
-        return 0;                /* keep compiler quiet */
+        return bitncmp(ip_addr(a1), ip_addr(a2), ip_maxbits(a1));
     }
+
+    return ip_family(a1) - ip_family(a2);
 }

 Datum
@@ -304,18 +315,13 @@
     inet       *a1 = PG_GETARG_INET_P(0);
     inet       *a2 = PG_GETARG_INET_P(1);

-    if ((ip_family(a1) == AF_INET) && (ip_family(a2) == AF_INET))
+    if (ip_family(a1) == ip_family(a2))
     {
         PG_RETURN_BOOL(ip_bits(a1) > ip_bits(a2)
-           && v4bitncmp(ip_v4addr(a1), ip_v4addr(a2), ip_bits(a2)) == 0);
-    }
-    else
-    {
-        /* Go for an IPV6 address here, before faulting out: */
-        elog(ERROR, "cannot compare address families %d and %d",
-             ip_family(a1), ip_family(a2));
-        PG_RETURN_BOOL(false);
+           && bitncmp(ip_addr(a1), ip_addr(a2), ip_bits(a2)) == 0);
     }
+
+    PG_RETURN_BOOL(false);
 }

 Datum
@@ -324,18 +330,13 @@
     inet       *a1 = PG_GETARG_INET_P(0);
     inet       *a2 = PG_GETARG_INET_P(1);

-    if ((ip_family(a1) == AF_INET) && (ip_family(a2) == AF_INET))
+    if (ip_family(a1) == ip_family(a2))
     {
         PG_RETURN_BOOL(ip_bits(a1) >= ip_bits(a2)
-           && v4bitncmp(ip_v4addr(a1), ip_v4addr(a2), ip_bits(a2)) == 0);
-    }
-    else
-    {
-        /* Go for an IPV6 address here, before faulting out: */
-        elog(ERROR, "cannot compare address families %d and %d",
-             ip_family(a1), ip_family(a2));
-        PG_RETURN_BOOL(false);
+           && bitncmp(ip_addr(a1), ip_addr(a2), ip_bits(a2)) == 0);
     }
+
+    PG_RETURN_BOOL(false);
 }

 Datum
@@ -344,18 +345,13 @@
     inet       *a1 = PG_GETARG_INET_P(0);
     inet       *a2 = PG_GETARG_INET_P(1);

-    if ((ip_family(a1) == AF_INET) && (ip_family(a2) == AF_INET))
+    if (ip_family(a1) == ip_family(a2))
     {
         PG_RETURN_BOOL(ip_bits(a1) < ip_bits(a2)
-           && v4bitncmp(ip_v4addr(a1), ip_v4addr(a2), ip_bits(a1)) == 0);
-    }
-    else
-    {
-        /* Go for an IPV6 address here, before faulting out: */
-        elog(ERROR, "cannot compare address families %d and %d",
-             ip_family(a1), ip_family(a2));
-        PG_RETURN_BOOL(false);
+           && bitncmp(ip_addr(a1), ip_addr(a2), ip_bits(a1)) == 0);
     }
+
+    PG_RETURN_BOOL(false);
 }

 Datum
@@ -364,18 +360,13 @@
     inet       *a1 = PG_GETARG_INET_P(0);
     inet       *a2 = PG_GETARG_INET_P(1);

-    if ((ip_family(a1) == AF_INET) && (ip_family(a2) == AF_INET))
+    if (ip_family(a1) == ip_family(a2))
     {
         PG_RETURN_BOOL(ip_bits(a1) <= ip_bits(a2)
-           && v4bitncmp(ip_v4addr(a1), ip_v4addr(a2), ip_bits(a1)) == 0);
-    }
-    else
-    {
-        /* Go for an IPV6 address here, before faulting out: */
-        elog(ERROR, "cannot compare address families %d and %d",
-             ip_family(a1), ip_family(a2));
-        PG_RETURN_BOOL(false);
+           && bitncmp(ip_addr(a1), ip_addr(a2), ip_bits(a1)) == 0);
     }
+
+    PG_RETURN_BOOL(false);
 }

 /*
@@ -387,19 +378,13 @@
     inet       *ip = PG_GETARG_INET_P(0);
     text       *ret;
     int            len;
-    char       *ptr,
-                tmp[sizeof("255.255.255.255/32")];
+    char       *ptr;
+    char        tmp[sizeof("xxxx:xxxx:xxxx:xxxx:xxxx:xxxx:255.255.255.255/128")];

-    if (ip_family(ip) == AF_INET)
-    {
-        /* It's an IP V4 address: */
-        /* force display of 32 bits, regardless of masklen... */
-        if (inet_net_ntop(AF_INET, &ip_v4addr(ip), 32, tmp, sizeof(tmp)) == NULL)
-            elog(ERROR, "unable to print host (%s)", strerror(errno));
-    }
-    else
-        /* Go for an IPV6 address here, before faulting out: */
-        elog(ERROR, "unknown address family (%d)", ip_family(ip));
+    /* force display of max bits, regardless of masklen... */
+    if (inet_net_ntop(ip_family(ip), ip_addr(ip), ip_maxbits(ip),
+              tmp, sizeof(tmp)) == NULL)
+        elog(ERROR, "unable to print host (%s)", strerror(errno));

     /* Suppress /n if present (shouldn't happen now) */
     if ((ptr = strchr(tmp, '/')) != NULL)
@@ -419,24 +404,17 @@
     inet       *ip = PG_GETARG_INET_P(0);
     text       *ret;
     int            len;
-    char        tmp[sizeof("255.255.255.255/32")];
+    char        tmp[sizeof("xxxx:xxxx:xxxx:xxxx:xxxx:xxxx:255.255.255.255/128")];

-    if (ip_family(ip) == AF_INET)
+    if (inet_net_ntop(ip_family(ip), ip_addr(ip), ip_maxbits(ip),
+              tmp, sizeof(tmp)) == NULL)
+        elog(ERROR, "unable to print host (%s)", strerror(errno));
+    /* Add /n if not present (which it won't be) */
+    if (strchr(tmp, '/') == NULL)
     {
-        /* It's an IP V4 address: */
-        /* force display of 32 bits, regardless of masklen... */
-        if (inet_net_ntop(AF_INET, &ip_v4addr(ip), 32, tmp, sizeof(tmp)) == NULL)
-            elog(ERROR, "unable to print host (%s)", strerror(errno));
-        /* Add /n if not present (which it won't be) */
-        if (strchr(tmp, '/') == NULL)
-        {
-            len = strlen(tmp);
-            snprintf(tmp + len, sizeof(tmp) - len, "/%u", ip_bits(ip));
-        }
+        len = strlen(tmp);
+        snprintf(tmp + len, sizeof(tmp) - len, "/%u", ip_bits(ip));
     }
-    else
-        /* Go for an IPV6 address here, before faulting out: */
-        elog(ERROR, "unknown address family (%d)", ip_family(ip));

     /* Return string as a text datum */
     len = strlen(tmp);
@@ -453,24 +431,18 @@
     text       *ret;
     char       *dst;
     int            len;
-    char        tmp[sizeof("255.255.255.255/32")];
-
-    if (ip_family(ip) == AF_INET)
-    {
-        /* It's an IP V4 address: */
-        if (ip_type(ip))
-            dst = inet_cidr_ntop(AF_INET, &ip_v4addr(ip), ip_bits(ip),
-                                 tmp, sizeof(tmp));
-        else
-            dst = inet_net_ntop(AF_INET, &ip_v4addr(ip), ip_bits(ip),
-                                tmp, sizeof(tmp));
+    char        tmp[sizeof("xxxx:xxxx:xxxx:xxxx:xxxx:xxxx:255.255.255.255/128")];

-        if (dst == NULL)
-            elog(ERROR, "unable to print address (%s)", strerror(errno));
-    }
-    else
-        /* Go for an IPV6 address here, before faulting out: */
-        elog(ERROR, "unknown address family (%d)", ip_family(ip));
+    if (ip_type(ip))
+        dst = inet_cidr_ntop(ip_family(ip), ip_addr(ip),
+                     ip_bits(ip), tmp, sizeof(tmp));
+    else
+        dst = inet_net_ntop(ip_family(ip), ip_addr(ip),
+                    ip_bits(ip), tmp, sizeof(tmp));
+
+    if (dst == NULL)
+        elog(ERROR, "unable to print address (%s)",
+             strerror(errno));

     /* Return string as a text datum */
     len = strlen(tmp);
@@ -489,40 +461,67 @@
 }

 Datum
+network_family(PG_FUNCTION_ARGS)
+{
+    inet       *ip = PG_GETARG_INET_P(0);
+
+    switch (ip_family(ip)) {
+    case PGSQL_AF_INET:
+        PG_RETURN_INT32(4);
+        break;
+    case PGSQL_AF_INET6:
+        PG_RETURN_INT32(6);
+        break;
+    default:
+        PG_RETURN_INT32(0);
+        break;
+    }
+}
+
+Datum
 network_broadcast(PG_FUNCTION_ARGS)
 {
     inet       *ip = PG_GETARG_INET_P(0);
     inet       *dst;
+    int byte;
+    int bits;
+    int maxbytes;
+    unsigned char mask;
+    unsigned char *a, *b;

     dst = (inet *) palloc(VARHDRSZ + sizeof(inet_struct));
     /* make sure any unused bits are zeroed */
     MemSet(dst, 0, VARHDRSZ + sizeof(inet_struct));

-    if (ip_family(ip) == AF_INET)
-    {
-        /* It's an IP V4 address: */
-        unsigned long mask = 0xffffffff;
-
-        /*
-         * Shifting by 32 or more bits does not yield portable results, so
-         * don't try it.
-         */
-        if (ip_bits(ip) < 32)
-            mask >>= ip_bits(ip);
-        else
-            mask = 0;
-
-        ip_v4addr(dst) = htonl(ntohl(ip_v4addr(ip)) | mask);
+    if (ip_family(ip) == PGSQL_AF_INET) {
+        maxbytes = 4;
+    } else {
+        maxbytes = 16;
+    }
+
+    bits = ip_bits(ip);
+    a = ip_addr(ip);
+    b = ip_addr(dst);
+
+    for (byte = 0 ; byte < maxbytes ; byte++) {
+        if (bits >= 8) {
+            mask = 0x00;
+            bits -= 8;
+        } else if (bits == 0) {
+            mask = 0xff;
+        } else {
+            mask = 0xff >> bits;
+            bits = 0;
+        }
+
+        b[byte] = a[byte] | mask;
     }
-    else
-        /* Go for an IPV6 address here, before faulting out: */
-        elog(ERROR, "unknown address family (%d)", ip_family(ip));

     ip_family(dst) = ip_family(ip);
     ip_bits(dst) = ip_bits(ip);
     ip_type(dst) = 0;
     VARATT_SIZEP(dst) = VARHDRSZ
-        + ((char *) &ip_v4addr(dst) - (char *) VARDATA(dst))
+        + ((char *)ip_addr(dst) - (char *)VARDATA(dst))
         + ip_addrsize(dst);

     PG_RETURN_INET_P(dst);
@@ -533,36 +532,45 @@
 {
     inet       *ip = PG_GETARG_INET_P(0);
     inet       *dst;
+    int byte;
+    int bits;
+    int maxbytes;
+    unsigned char mask;
+    unsigned char *a, *b;

     dst = (inet *) palloc(VARHDRSZ + sizeof(inet_struct));
     /* make sure any unused bits are zeroed */
     MemSet(dst, 0, VARHDRSZ + sizeof(inet_struct));

-    if (ip_family(ip) == AF_INET)
-    {
-        /* It's an IP V4 address: */
-        unsigned long mask = 0xffffffff;
-
-        /*
-         * Shifting by 32 or more bits does not yield portable results, so
-         * don't try it.
-         */
-        if (ip_bits(ip) > 0)
-            mask <<= (32 - ip_bits(ip));
-        else
-            mask = 0;
-
-        ip_v4addr(dst) = htonl(ntohl(ip_v4addr(ip)) & mask);
+    if (ip_family(ip) == PGSQL_AF_INET) {
+        maxbytes = 4;
+    } else {
+        maxbytes = 16;
+    }
+
+    bits = ip_bits(ip);
+    a = ip_addr(ip);
+    b = ip_addr(dst);
+
+    byte = 0;
+    while (bits) {
+        if (bits >= 8) {
+            mask = 0xff;
+            bits -= 8;
+        } else {
+            mask = 0xff << (8 - bits);
+            bits = 0;
+        }
+
+        b[byte] = a[byte] & mask;
+        byte++;
     }
-    else
-        /* Go for an IPV6 address here, before faulting out: */
-        elog(ERROR, "unknown address family (%d)", ip_family(ip));

     ip_family(dst) = ip_family(ip);
     ip_bits(dst) = ip_bits(ip);
     ip_type(dst) = 1;
     VARATT_SIZEP(dst) = VARHDRSZ
-        + ((char *) &ip_v4addr(dst) - (char *) VARDATA(dst))
+        + ((char *)ip_addr(dst) - (char *)VARDATA(dst))
         + ip_addrsize(dst);

     PG_RETURN_INET_P(dst);
@@ -573,37 +581,44 @@
 {
     inet       *ip = PG_GETARG_INET_P(0);
     inet       *dst;
+    int byte;
+    int bits;
+    int maxbytes;
+    unsigned char mask;
+    unsigned char *b;

     dst = (inet *) palloc(VARHDRSZ + sizeof(inet_struct));
     /* make sure any unused bits are zeroed */
     MemSet(dst, 0, VARHDRSZ + sizeof(inet_struct));

-    if (ip_family(ip) == AF_INET)
-    {
-        /* It's an IP V4 address: */
-        unsigned long mask = 0xffffffff;
-
-        /*
-         * Shifting by 32 or more bits does not yield portable results, so
-         * don't try it.
-         */
-        if (ip_bits(ip) > 0)
-            mask <<= (32 - ip_bits(ip));
-        else
-            mask = 0;
-
-        ip_v4addr(dst) = htonl(mask);
-
-        ip_bits(dst) = 32;
+    if (ip_family(ip) == PGSQL_AF_INET) {
+        maxbytes = 4;
+    } else {
+        maxbytes = 16;
+    }
+
+    bits = ip_bits(ip);
+    b = ip_addr(dst);
+
+    byte = 0;
+    while (bits) {
+        if (bits >= 8) {
+            mask = 0xff;
+            bits -= 8;
+        } else {
+            mask = 0xff << (8 - bits);
+            bits = 0;
+        }
+
+        b[byte] = mask;
+        byte++;
     }
-    else
-        /* Go for an IPV6 address here, before faulting out: */
-        elog(ERROR, "unknown address family (%d)", ip_family(ip));

     ip_family(dst) = ip_family(ip);
+    ip_bits(dst) = ip_bits(ip);
     ip_type(dst) = 0;
     VARATT_SIZEP(dst) = VARHDRSZ
-        + ((char *) &ip_v4addr(dst) - (char *) VARDATA(dst))
+        + ((char *)ip_addr(dst) - (char *)VARDATA(dst))
         + ip_addrsize(dst);

     PG_RETURN_INET_P(dst);
@@ -629,12 +644,26 @@
         case CIDROID:
             {
                 inet       *ip = DatumGetInetP(value);
-
-                if (ip_family(ip) == AF_INET)
-                    return (double) ip_v4addr(ip);
+                int len;
+                double res;
+                int i;
+
+                /*
+                 * Note that we don't use the full address
+                 * here.
+                 */
+                if (ip_family(ip) == PGSQL_AF_INET)
+                    len = 4;
                 else
-                    /* Go for an IPV6 address here, before faulting out: */
-                    elog(ERROR, "unknown address family (%d)", ip_family(ip));
+                    len = 5;
+
+                res = ip_family(ip);
+                for (i = 0 ; i < len ; i++) {
+                    res *= 256;
+                    res += ip_addr(ip)[i];
+                }
+                return res;
+
                 break;
             }
         case MACADDROID:
@@ -657,53 +686,78 @@
     return 0;
 }

-
 /*
- *    Bitwise comparison for V4 addresses.  Add V6 implementation!
+ * int
+ * bitncmp(l, r, n)
+ *      compare bit masks l and r, for n bits.
+ * return:
+ *      -1, 1, or 0 in the libc tradition.
+ * note:
+ *      network byte order assumed.  this means 192.5.5.240/28 has
+ *      0x11110000 in its fourth octet.
+ * author:
+ *      Paul Vixie (ISC), June 1996
  */
-
 static int
-v4bitncmp(unsigned long a1, unsigned long a2, int bits)
+bitncmp(void *l, void *r, int n)
 {
-    unsigned long mask;
+    u_int lb, rb;
+    int x, b;

-    /*
-     * Shifting by 32 or more bits does not yield portable results, so
-     * don't try it.
-     */
-    if (bits > 0)
-        mask = (0xFFFFFFFFL << (32 - bits)) & 0xFFFFFFFFL;
-    else
-        mask = 0;
-    a1 = ntohl(a1);
-    a2 = ntohl(a2);
-    if ((a1 & mask) < (a2 & mask))
-        return (-1);
-    else if ((a1 & mask) > (a2 & mask))
-        return (1);
+    b = n / 8;
+    x = memcmp(l, r, b);
+    if (x)
+        return (x);
+
+    lb = ((const u_char *)l)[b];
+    rb = ((const u_char *)r)[b];
+    for (b = n % 8; b > 0; b--) {
+        if ((lb & 0x80) != (rb & 0x80)) {
+            if (lb & 0x80)
+                return (1);
+            return (-1);
+        }
+        lb <<= 1;
+        rb <<= 1;
+    }
     return (0);
 }

-/*
- * Returns true if given address fits fully within the specified bit width.
- */
 static bool
-v4addressOK(unsigned long a1, int bits)
+addressOK(unsigned char *a, int bits, int family)
 {
-    unsigned long mask;
+    int byte;
+    int nbits;
+    int maxbits;
+    int maxbytes;
+    unsigned char mask;
+
+    if (family == PGSQL_AF_INET) {
+        maxbits = 32;
+        maxbytes = 4;
+    } else {
+        maxbits = 128;
+        maxbytes = 16;
+    }
+    assert(bits <= maxbits);
+
+    if (bits == maxbits)
+        return 1;
+
+    byte = (bits + 7) / 8;
+    nbits = bits % 8;
+    mask = 0xff;
+    if (bits != 0)
+        mask >>= nbits;
+
+    while (byte < maxbytes) {
+        if ((a[byte] & mask) != 0)
+            return 0;
+        mask = 0xff;
+        byte++;
+    }

-    /*
-     * Shifting by 32 or more bits does not yield portable results, so
-     * don't try it.
-     */
-    if (bits > 0)
-        mask = (0xFFFFFFFFL << (32 - bits)) & 0xFFFFFFFFL;
-    else
-        mask = 0;
-    a1 = ntohl(a1);
-    if ((a1 & mask) == a1)
-        return true;
-    return false;
+    return 1;
 }


@@ -721,15 +775,16 @@

 /*
  * return "last" IP on a given network. It's the broadcast address,
- * however, masklen has to be set to 32, since
+ * however, masklen has to be set to its max btis, since
  * 192.168.0.255/24 is considered less than 192.168.0.255/32
  *
- * NB: this is not IPv6 ready ...
+ * inet_set_masklen() hacked to max out the masklength to 128 for IPv6
+ * and 32 for IPv4 when given '-1' as argument.
  */
 Datum
 network_scan_last(Datum in)
 {
     return DirectFunctionCall2(inet_set_masklen,
                                DirectFunctionCall1(network_broadcast, in),
-                               Int32GetDatum(32));
+               Int32GetDatum(-1));
 }
diff -ur orig-postgresql-7.3.2/src/include/catalog/pg_proc.h postgresql-7.3.2/src/include/catalog/pg_proc.h
--- orig-postgresql-7.3.2/src/include/catalog/pg_proc.h    2002-11-02 10:41:22.000000000 -0800
+++ postgresql-7.3.2/src/include/catalog/pg_proc.h    2003-04-22 13:02:07.000000000 -0700
@@ -2353,6 +2353,8 @@
 DESCR("show address octets only");
 DATA(insert OID = 730 (  text                PGNSP PGUID 12 f f t f i 1 25 "869"  network_show - _null_ ));
 DESCR("show all parts of inet/cidr value");
+DATA(insert OID = 731 (  family                PGNSP PGUID 12 f f t f i 1 23 "869"  network_family - _null_ ));
+DESCR("return address family (4 for IPv4, 6 for IPv6)");
 DATA(insert OID = 1713 (  inet                PGNSP PGUID 12 f f t f i 1 869 "25"  text_inet - _null_ ));
 DESCR("text to inet");
 DATA(insert OID = 1714 (  cidr                PGNSP PGUID 12 f f t f i 1 650 "25"  text_cidr - _null_ ));
diff -ur orig-postgresql-7.3.2/src/include/utils/builtins.h postgresql-7.3.2/src/include/utils/builtins.h
--- orig-postgresql-7.3.2/src/include/utils/builtins.h    2002-11-02 10:41:22.000000000 -0800
+++ postgresql-7.3.2/src/include/utils/builtins.h    2003-04-22 13:02:07.000000000 -0700
@@ -566,6 +566,7 @@
 extern Datum network_network(PG_FUNCTION_ARGS);
 extern Datum network_netmask(PG_FUNCTION_ARGS);
 extern Datum network_masklen(PG_FUNCTION_ARGS);
+extern Datum network_family(PG_FUNCTION_ARGS);
 extern Datum network_broadcast(PG_FUNCTION_ARGS);
 extern Datum network_host(PG_FUNCTION_ARGS);
 extern Datum network_show(PG_FUNCTION_ARGS);
diff -ur orig-postgresql-7.3.2/src/include/utils/inet.h postgresql-7.3.2/src/include/utils/inet.h
--- orig-postgresql-7.3.2/src/include/utils/inet.h    2002-06-20 13:29:53.000000000 -0700
+++ postgresql-7.3.2/src/include/utils/inet.h    2003-04-22 13:02:07.000000000 -0700
@@ -23,14 +23,20 @@
     unsigned char family;
     unsigned char bits;
     unsigned char type;
-    union
-    {
-        unsigned int ipv4_addr; /* network byte order */
-        /* add IPV6 address type here */
-    }            addr;
+    unsigned char ip_addr[16]; /* 128 bits of address */
 } inet_struct;

 /*
+ * Referencing all of the non-AF_INET types to AF_INET lets us work on
+ * machines which may not have the appropriate address family (like
+ * inet6 addresses when AF_INET6 isn't present) but doesn't cause a
+ * dump/reload requirement.  Existing databases used AF_INET for the family
+ * type on disk.
+ */
+#define PGSQL_AF_INET    (AF_INET + 0)
+#define PGSQL_AF_INET6    (AF_INET + 1)
+
+/*
  * Both INET and CIDR addresses are represented within Postgres as varlena
  * objects, ie, there is a varlena header (basically a length word) in front
  * of the struct type depicted above.
diff -ur orig-postgresql-7.3.2/src/test/regress/expected/inet.out postgresql-7.3.2/src/test/regress/expected/inet.out
--- orig-postgresql-7.3.2/src/test/regress/expected/inet.out    2001-06-16 19:05:20.000000000 -0700
+++ postgresql-7.3.2/src/test/regress/expected/inet.out    2003-04-22 16:48:02.000000000 -0700
@@ -19,110 +19,132 @@
 INSERT INTO INET_TBL (c, i) VALUES ('10', '10.1.2.3/8');
 INSERT INTO INET_TBL (c, i) VALUES ('10', '11.1.2.3/8');
 INSERT INTO INET_TBL (c, i) VALUES ('10', '9.1.2.3/8');
+INSERT INTO INET_TBL (c, i) VALUES ('10:23::f1', '10:23::f1/64');
+INSERT INTO INET_TBL (c, i) VALUES ('10:23::8000/113', '10:23::ffff');
+INSERT INTO INET_TBL (c, i) VALUES ('::ffff:1.2.3.4', '::4.3.2.1/24');
 -- check that CIDR rejects invalid input:
 INSERT INTO INET_TBL (c, i) VALUES ('192.168.1.2/24', '192.168.1.226');
 ERROR:  invalid CIDR value '192.168.1.2/24': has bits set to right of mask
+INSERT INTO INET_TBL (c, i) VALUES ('1234::1234::1234', '::1.2.3.4');
+ERROR:  invalid CIDR value '1234::1234::1234'
 -- check that CIDR rejects invalid input when converting from text:
 INSERT INTO INET_TBL (c, i) VALUES (cidr('192.168.1.2/24'), '192.168.1.226');
 ERROR:  invalid CIDR value '192.168.1.2/24': has bits set to right of mask
+INSERT INTO INET_TBL (c, i) VALUES (cidr('ffff:ffff:ffff:ffff::/24'), '::192.168.1.226');
+ERROR:  invalid CIDR value 'ffff:ffff:ffff:ffff::/24': has bits set to right of mask
 SELECT '' AS ten, c AS cidr, i AS inet FROM INET_TBL;
- ten |      cidr      |       inet
------+----------------+------------------
-     | 192.168.1.0/24 | 192.168.1.226/24
-     | 192.168.1.0/24 | 192.168.1.226
-     | 192.168.1.0/24 | 192.168.1.0/24
-     | 192.168.1.0/24 | 192.168.1.0/25
-     | 192.168.1.0/24 | 192.168.1.255/24
-     | 192.168.1.0/24 | 192.168.1.255/25
-     | 10.0.0.0/8     | 10.1.2.3/8
-     | 10.0.0.0/32    | 10.1.2.3/8
-     | 10.1.2.3/32    | 10.1.2.3
-     | 10.1.2.0/24    | 10.1.2.3/24
-     | 10.1.0.0/16    | 10.1.2.3/16
-     | 10.0.0.0/8     | 10.1.2.3/8
-     | 10.0.0.0/8     | 11.1.2.3/8
-     | 10.0.0.0/8     | 9.1.2.3/8
-(14 rows)
+ ten |      cidr          |       inet
+-----+--------------------+------------------
+     | 192.168.1.0/24     | 192.168.1.226/24
+     | 192.168.1.0/24     | 192.168.1.226
+     | 192.168.1.0/24     | 192.168.1.0/24
+     | 192.168.1.0/24     | 192.168.1.0/25
+     | 192.168.1.0/24     | 192.168.1.255/24
+     | 192.168.1.0/24     | 192.168.1.255/25
+     | 10.0.0.0/8         | 10.1.2.3/8
+     | 10.0.0.0/32        | 10.1.2.3/8
+     | 10.1.2.3/32        | 10.1.2.3
+     | 10.1.2.0/24        | 10.1.2.3/24
+     | 10.1.0.0/16        | 10.1.2.3/16
+     | 10.0.0.0/8         | 10.1.2.3/8
+     | 10.0.0.0/8         | 11.1.2.3/8
+     | 10.0.0.0/8         | 9.1.2.3/8
+     | 10:23::f1/128      | 10:23::f1/64
+     | 10:23::8000/113    | 10:23::ffff
+     | ::ffff:1.2.3.4/128 | ::4.3.2.1/24
+(17 rows)

 -- now test some support functions
-SELECT '' AS ten, i AS inet, host(i), text(i) FROM INET_TBL;
- ten |       inet       |     host      |       text
------+------------------+---------------+------------------
-     | 192.168.1.226/24 | 192.168.1.226 | 192.168.1.226/24
-     | 192.168.1.226    | 192.168.1.226 | 192.168.1.226/32
-     | 192.168.1.0/24   | 192.168.1.0   | 192.168.1.0/24
-     | 192.168.1.0/25   | 192.168.1.0   | 192.168.1.0/25
-     | 192.168.1.255/24 | 192.168.1.255 | 192.168.1.255/24
-     | 192.168.1.255/25 | 192.168.1.255 | 192.168.1.255/25
-     | 10.1.2.3/8       | 10.1.2.3      | 10.1.2.3/8
-     | 10.1.2.3/8       | 10.1.2.3      | 10.1.2.3/8
-     | 10.1.2.3         | 10.1.2.3      | 10.1.2.3/32
-     | 10.1.2.3/24      | 10.1.2.3      | 10.1.2.3/24
-     | 10.1.2.3/16      | 10.1.2.3      | 10.1.2.3/16
-     | 10.1.2.3/8       | 10.1.2.3      | 10.1.2.3/8
-     | 11.1.2.3/8       | 11.1.2.3      | 11.1.2.3/8
-     | 9.1.2.3/8        | 9.1.2.3       | 9.1.2.3/8
-(14 rows)
+SELECT '' AS ten, i AS inet, host(i), text(i), family(i) FROM INET_TBL;
+ ten |       inet       |     host      |       text       | family
+-----+------------------+---------------+------------------+--------
+     | 192.168.1.226/24 | 192.168.1.226 | 192.168.1.226/24 |      4
+     | 192.168.1.226    | 192.168.1.226 | 192.168.1.226/32 |      4
+     | 192.168.1.0/24   | 192.168.1.0   | 192.168.1.0/24   |      4
+     | 192.168.1.0/25   | 192.168.1.0   | 192.168.1.0/25   |      4
+     | 192.168.1.255/24 | 192.168.1.255 | 192.168.1.255/24 |      4
+     | 192.168.1.255/25 | 192.168.1.255 | 192.168.1.255/25 |      4
+     | 10.1.2.3/8       | 10.1.2.3      | 10.1.2.3/8       |      4
+     | 10.1.2.3/8       | 10.1.2.3      | 10.1.2.3/8       |      4
+     | 10.1.2.3         | 10.1.2.3      | 10.1.2.3/32      |      4
+     | 10.1.2.3/24      | 10.1.2.3      | 10.1.2.3/24      |      4
+     | 10.1.2.3/16      | 10.1.2.3      | 10.1.2.3/16      |      4
+     | 10.1.2.3/8       | 10.1.2.3      | 10.1.2.3/8       |      4
+     | 11.1.2.3/8       | 11.1.2.3      | 11.1.2.3/8       |      4
+     | 9.1.2.3/8        | 9.1.2.3       | 9.1.2.3/8        |      4
+     | 10:23::f1/64     | 10:23::f1     | 10:23::f1/64     |      6
+     | 10:23::ffff      | 10:23::ffff   | 10:23::ffff/128  |      6
+     | ::4.3.2.1/24     | ::4.3.2.1     | ::4.3.2.1/24     |      6
+(17 rows)

 SELECT '' AS ten, c AS cidr, broadcast(c),
   i AS inet, broadcast(i) FROM INET_TBL;
- ten |      cidr      |    broadcast     |       inet       |    broadcast
------+----------------+------------------+------------------+------------------
-     | 192.168.1.0/24 | 192.168.1.255/24 | 192.168.1.226/24 | 192.168.1.255/24
-     | 192.168.1.0/24 | 192.168.1.255/24 | 192.168.1.226    | 192.168.1.226
-     | 192.168.1.0/24 | 192.168.1.255/24 | 192.168.1.0/24   | 192.168.1.255/24
-     | 192.168.1.0/24 | 192.168.1.255/24 | 192.168.1.0/25   | 192.168.1.127/25
-     | 192.168.1.0/24 | 192.168.1.255/24 | 192.168.1.255/24 | 192.168.1.255/24
-     | 192.168.1.0/24 | 192.168.1.255/24 | 192.168.1.255/25 | 192.168.1.255/25
-     | 10.0.0.0/8     | 10.255.255.255/8 | 10.1.2.3/8       | 10.255.255.255/8
-     | 10.0.0.0/32    | 10.0.0.0         | 10.1.2.3/8       | 10.255.255.255/8
-     | 10.1.2.3/32    | 10.1.2.3         | 10.1.2.3         | 10.1.2.3
-     | 10.1.2.0/24    | 10.1.2.255/24    | 10.1.2.3/24      | 10.1.2.255/24
-     | 10.1.0.0/16    | 10.1.255.255/16  | 10.1.2.3/16      | 10.1.255.255/16
-     | 10.0.0.0/8     | 10.255.255.255/8 | 10.1.2.3/8       | 10.255.255.255/8
-     | 10.0.0.0/8     | 10.255.255.255/8 | 11.1.2.3/8       | 11.255.255.255/8
-     | 10.0.0.0/8     | 10.255.255.255/8 | 9.1.2.3/8        | 9.255.255.255/8
-(14 rows)
+ ten |      cidr          |    broadcast     |       inet       |               broadcast
+-----+--------------------+------------------+------------------+---------------------------------------
+     | 192.168.1.0/24     | 192.168.1.255/24 | 192.168.1.226/24 | 192.168.1.255/24
+     | 192.168.1.0/24     | 192.168.1.255/24 | 192.168.1.226    | 192.168.1.226
+     | 192.168.1.0/24     | 192.168.1.255/24 | 192.168.1.0/24   | 192.168.1.255/24
+     | 192.168.1.0/24     | 192.168.1.255/24 | 192.168.1.0/25   | 192.168.1.127/25
+     | 192.168.1.0/24     | 192.168.1.255/24 | 192.168.1.255/24 | 192.168.1.255/24
+     | 192.168.1.0/24     | 192.168.1.255/24 | 192.168.1.255/25 | 192.168.1.255/25
+     | 10.0.0.0/8         | 10.255.255.255/8 | 10.1.2.3/8       | 10.255.255.255/8
+     | 10.0.0.0/32        | 10.0.0.0         | 10.1.2.3/8       | 10.255.255.255/8
+     | 10.1.2.3/32        | 10.1.2.3         | 10.1.2.3         | 10.1.2.3
+     | 10.1.2.0/24        | 10.1.2.255/24    | 10.1.2.3/24      | 10.1.2.255/24
+     | 10.1.0.0/16        | 10.1.255.255/16  | 10.1.2.3/16      | 10.1.255.255/16
+     | 10.0.0.0/8         | 10.255.255.255/8 | 10.1.2.3/8       | 10.255.255.255/8
+     | 10.0.0.0/8         | 10.255.255.255/8 | 11.1.2.3/8       | 11.255.255.255/8
+     | 10.0.0.0/8         | 10.255.255.255/8 | 9.1.2.3/8        | 9.255.255.255/8
+     | 10:23::f1/128      | 10:23::f1        | 10:23::f1/64     | 10:23::ffff:ffff:ffff:ffff/64
+     | 10:23::8000/113    | 10:23::ffff/113  | 10:23::ffff      | 10:23::ffff
+     | ::ffff:1.2.3.4/128 | ::ffff:1.2.3.4   | ::4.3.2.1/24     | 0:ff:ffff:ffff:ffff:ffff:ffff:ffff/24
+(17 rows)

 SELECT '' AS ten, c AS cidr, network(c) AS "network(cidr)",
   i AS inet, network(i) AS "network(inet)" FROM INET_TBL;
- ten |      cidr      | network(cidr)  |       inet       |  network(inet)
------+----------------+----------------+------------------+------------------
-     | 192.168.1.0/24 | 192.168.1.0/24 | 192.168.1.226/24 | 192.168.1.0/24
-     | 192.168.1.0/24 | 192.168.1.0/24 | 192.168.1.226    | 192.168.1.226/32
-     | 192.168.1.0/24 | 192.168.1.0/24 | 192.168.1.0/24   | 192.168.1.0/24
-     | 192.168.1.0/24 | 192.168.1.0/24 | 192.168.1.0/25   | 192.168.1.0/25
-     | 192.168.1.0/24 | 192.168.1.0/24 | 192.168.1.255/24 | 192.168.1.0/24
-     | 192.168.1.0/24 | 192.168.1.0/24 | 192.168.1.255/25 | 192.168.1.128/25
-     | 10.0.0.0/8     | 10.0.0.0/8     | 10.1.2.3/8       | 10.0.0.0/8
-     | 10.0.0.0/32    | 10.0.0.0/32    | 10.1.2.3/8       | 10.0.0.0/8
-     | 10.1.2.3/32    | 10.1.2.3/32    | 10.1.2.3         | 10.1.2.3/32
-     | 10.1.2.0/24    | 10.1.2.0/24    | 10.1.2.3/24      | 10.1.2.0/24
-     | 10.1.0.0/16    | 10.1.0.0/16    | 10.1.2.3/16      | 10.1.0.0/16
-     | 10.0.0.0/8     | 10.0.0.0/8     | 10.1.2.3/8       | 10.0.0.0/8
-     | 10.0.0.0/8     | 10.0.0.0/8     | 11.1.2.3/8       | 11.0.0.0/8
-     | 10.0.0.0/8     | 10.0.0.0/8     | 9.1.2.3/8        | 9.0.0.0/8
-(14 rows)
+ ten |        cidr        |  network(cidr)     |       inet       |  network(inet)
+-----+--------------------+--------------------+------------------+------------------
+     | 192.168.1.0/24     | 192.168.1.0/24     | 192.168.1.226/24 | 192.168.1.0/24
+     | 192.168.1.0/24     | 192.168.1.0/24     | 192.168.1.226    | 192.168.1.226/32
+     | 192.168.1.0/24     | 192.168.1.0/24     | 192.168.1.0/24   | 192.168.1.0/24
+     | 192.168.1.0/24     | 192.168.1.0/24     | 192.168.1.0/25   | 192.168.1.0/25
+     | 192.168.1.0/24     | 192.168.1.0/24     | 192.168.1.255/24 | 192.168.1.0/24
+     | 192.168.1.0/24     | 192.168.1.0/24     | 192.168.1.255/25 | 192.168.1.128/25
+     | 10.0.0.0/8         | 10.0.0.0/8         | 10.1.2.3/8       | 10.0.0.0/8
+     | 10.0.0.0/32        | 10.0.0.0/32        | 10.1.2.3/8       | 10.0.0.0/8
+     | 10.1.2.3/32        | 10.1.2.3/32        | 10.1.2.3         | 10.1.2.3/32
+     | 10.1.2.0/24        | 10.1.2.0/24        | 10.1.2.3/24      | 10.1.2.0/24
+     | 10.1.0.0/16        | 10.1.0.0/16        | 10.1.2.3/16      | 10.1.0.0/16
+     | 10.0.0.0/8         | 10.0.0.0/8         | 10.1.2.3/8       | 10.0.0.0/8
+     | 10.0.0.0/8         | 10.0.0.0/8         | 11.1.2.3/8       | 11.0.0.0/8
+     | 10.0.0.0/8         | 10.0.0.0/8         | 9.1.2.3/8        | 9.0.0.0/8
+     | 10:23::f1/128      | 10:23::f1/128      | 10:23::f1/64     | 10:23::/64
+     | 10:23::8000/113    | 10:23::8000/113    | 10:23::ffff      | 10:23::ffff/128
+     | ::ffff:1.2.3.4/128 | ::ffff:1.2.3.4/128 | ::4.3.2.1/24     | ::/24
+(17 rows)

 SELECT '' AS ten, c AS cidr, masklen(c) AS "masklen(cidr)",
   i AS inet, masklen(i) AS "masklen(inet)" FROM INET_TBL;
- ten |      cidr      | masklen(cidr) |       inet       | masklen(inet)
------+----------------+---------------+------------------+---------------
-     | 192.168.1.0/24 |            24 | 192.168.1.226/24 |            24
-     | 192.168.1.0/24 |            24 | 192.168.1.226    |            32
-     | 192.168.1.0/24 |            24 | 192.168.1.0/24   |            24
-     | 192.168.1.0/24 |            24 | 192.168.1.0/25   |            25
-     | 192.168.1.0/24 |            24 | 192.168.1.255/24 |            24
-     | 192.168.1.0/24 |            24 | 192.168.1.255/25 |            25
-     | 10.0.0.0/8     |             8 | 10.1.2.3/8       |             8
-     | 10.0.0.0/32    |            32 | 10.1.2.3/8       |             8
-     | 10.1.2.3/32    |            32 | 10.1.2.3         |            32
-     | 10.1.2.0/24    |            24 | 10.1.2.3/24      |            24
-     | 10.1.0.0/16    |            16 | 10.1.2.3/16      |            16
-     | 10.0.0.0/8     |             8 | 10.1.2.3/8       |             8
-     | 10.0.0.0/8     |             8 | 11.1.2.3/8       |             8
-     | 10.0.0.0/8     |             8 | 9.1.2.3/8        |             8
-(14 rows)
+ ten |        cidr        | masklen(cidr) |       inet       | masklen(inet)
+-----+--------------------+---------------+------------------+---------------
+     | 192.168.1.0/24     |            24 | 192.168.1.226/24 |            24
+     | 192.168.1.0/24     |            24 | 192.168.1.226    |            32
+     | 192.168.1.0/24     |            24 | 192.168.1.0/24   |            24
+     | 192.168.1.0/24     |            24 | 192.168.1.0/25   |            25
+     | 192.168.1.0/24     |            24 | 192.168.1.255/24 |            24
+     | 192.168.1.0/24     |            24 | 192.168.1.255/25 |            25
+     | 10.0.0.0/8         |             8 | 10.1.2.3/8       |             8
+     | 10.0.0.0/32        |            32 | 10.1.2.3/8       |             8
+     | 10.1.2.3/32        |            32 | 10.1.2.3         |            32
+     | 10.1.2.0/24        |            24 | 10.1.2.3/24      |            24
+     | 10.1.0.0/16        |            16 | 10.1.2.3/16      |            16
+     | 10.0.0.0/8         |             8 | 10.1.2.3/8       |             8
+     | 10.0.0.0/8         |             8 | 11.1.2.3/8       |             8
+     | 10.0.0.0/8         |             8 | 9.1.2.3/8        |             8
+     | 10:23::f1/128      |           128 | 10:23::f1/64     |            64
+     | 10:23::8000/113    |           113 | 10:23::ffff      |           128
+     | ::ffff:1.2.3.4/128 |           128 | ::4.3.2.1/24     |            24
+(17 rows)

 SELECT '' AS four, c AS cidr, masklen(c) AS "masklen(cidr)",
   i AS inet, masklen(i) AS "masklen(inet)" FROM INET_TBL
@@ -149,23 +171,26 @@
   i << c AS sb, i <<= c AS sbe,
   i >> c AS sup, i >>= c AS spe
   FROM INET_TBL;
- ten |        i         |       c        | lt | le | eq | ge | gt | ne | sb | sbe | sup | spe
------+------------------+----------------+----+----+----+----+----+----+----+-----+-----+-----
-     | 192.168.1.226/24 | 192.168.1.0/24 | f  | f  | f  | t  | t  | t  | f  | t   | f   | t
-     | 192.168.1.226    | 192.168.1.0/24 | f  | f  | f  | t  | t  | t  | t  | t   | f   | f
-     | 192.168.1.0/24   | 192.168.1.0/24 | f  | t  | t  | t  | f  | f  | f  | t   | f   | t
-     | 192.168.1.0/25   | 192.168.1.0/24 | f  | f  | f  | t  | t  | t  | t  | t   | f   | f
-     | 192.168.1.255/24 | 192.168.1.0/24 | f  | f  | f  | t  | t  | t  | f  | t   | f   | t
-     | 192.168.1.255/25 | 192.168.1.0/24 | f  | f  | f  | t  | t  | t  | t  | t   | f   | f
-     | 10.1.2.3/8       | 10.0.0.0/8     | f  | f  | f  | t  | t  | t  | f  | t   | f   | t
-     | 10.1.2.3/8       | 10.0.0.0/32    | t  | t  | f  | f  | f  | t  | f  | f   | t   | t
-     | 10.1.2.3         | 10.1.2.3/32    | f  | t  | t  | t  | f  | f  | f  | t   | f   | t
-     | 10.1.2.3/24      | 10.1.2.0/24    | f  | f  | f  | t  | t  | t  | f  | t   | f   | t
-     | 10.1.2.3/16      | 10.1.0.0/16    | f  | f  | f  | t  | t  | t  | f  | t   | f   | t
-     | 10.1.2.3/8       | 10.0.0.0/8     | f  | f  | f  | t  | t  | t  | f  | t   | f   | t
-     | 11.1.2.3/8       | 10.0.0.0/8     | f  | f  | f  | t  | t  | t  | f  | f   | f   | f
-     | 9.1.2.3/8        | 10.0.0.0/8     | t  | t  | f  | f  | f  | t  | f  | f   | f   | f
-(14 rows)
+ ten |        i         |       c            | lt | le | eq | ge | gt | ne | sb | sbe | sup | spe
+-----+------------------+--------------------+----+----+----+----+----+----+----+-----+-----+-----
+     | 192.168.1.226/24 | 192.168.1.0/24     | f  | f  | f  | t  | t  | t  | f  | t   | f   | t
+     | 192.168.1.226    | 192.168.1.0/24     | f  | f  | f  | t  | t  | t  | t  | t   | f   | f
+     | 192.168.1.0/24   | 192.168.1.0/24     | f  | t  | t  | t  | f  | f  | f  | t   | f   | t
+     | 192.168.1.0/25   | 192.168.1.0/24     | f  | f  | f  | t  | t  | t  | t  | t   | f   | f
+     | 192.168.1.255/24 | 192.168.1.0/24     | f  | f  | f  | t  | t  | t  | f  | t   | f   | t
+     | 192.168.1.255/25 | 192.168.1.0/24     | f  | f  | f  | t  | t  | t  | t  | t   | f   | f
+     | 10.1.2.3/8       | 10.0.0.0/8         | f  | f  | f  | t  | t  | t  | f  | t   | f   | t
+     | 10.1.2.3/8       | 10.0.0.0/32        | t  | t  | f  | f  | f  | t  | f  | f   | t   | t
+     | 10.1.2.3         | 10.1.2.3/32        | f  | t  | t  | t  | f  | f  | f  | t   | f   | t
+     | 10.1.2.3/24      | 10.1.2.0/24        | f  | f  | f  | t  | t  | t  | f  | t   | f   | t
+     | 10.1.2.3/16      | 10.1.0.0/16        | f  | f  | f  | t  | t  | t  | f  | t   | f   | t
+     | 10.1.2.3/8       | 10.0.0.0/8         | f  | f  | f  | t  | t  | t  | f  | t   | f   | t
+     | 11.1.2.3/8       | 10.0.0.0/8         | f  | f  | f  | t  | t  | t  | f  | f   | f   | f
+     | 9.1.2.3/8        | 10.0.0.0/8         | t  | t  | f  | f  | f  | t  | f  | f   | f   | f
+     | 10:23::f1/64     | 10:23::f1/128      | t  | t  | f  | f  | f  | t  | f  | f   | t   | t
+     | 10:23::ffff      | 10:23::8000/113    | f  | f  | f  | t  | t  | t  | t  | t   | f   | f
+     | ::4.3.2.1/24     | ::ffff:1.2.3.4/128 | t  | t  | f  | f  | f  | t  | f  | f   | t   | t
+(17 rows)

 -- check the conversion to/from text and set_netmask
 select '' AS ten, set_masklen(inet(text(i)), 24) FROM INET_TBL;
@@ -185,7 +210,10 @@
      | 10.1.2.3/24
      | 11.1.2.3/24
      | 9.1.2.3/24
-(14 rows)
+     | 10:23::f1/24
+     | 10:23::ffff/24
+     | ::4.3.2.1/24
+(17 rows)

 -- check that index works correctly
 create index inet_idx1 on inet_tbl(i);
diff -ur orig-postgresql-7.3.2/src/test/regress/sql/inet.sql postgresql-7.3.2/src/test/regress/sql/inet.sql
--- orig-postgresql-7.3.2/src/test/regress/sql/inet.sql    2001-06-16 19:05:20.000000000 -0700
+++ postgresql-7.3.2/src/test/regress/sql/inet.sql    2003-04-22 13:25:24.000000000 -0700
@@ -20,16 +20,20 @@
 INSERT INTO INET_TBL (c, i) VALUES ('10', '10.1.2.3/8');
 INSERT INTO INET_TBL (c, i) VALUES ('10', '11.1.2.3/8');
 INSERT INTO INET_TBL (c, i) VALUES ('10', '9.1.2.3/8');
+INSERT INTO INET_TBL (c, i) VALUES ('10:23::f1', '10:23::f1/64');
+INSERT INTO INET_TBL (c, i) VALUES ('10:23::8000/113', '10:23::ffff');
+INSERT INTO INET_TBL (c, i) VALUES ('::ffff:1.2.3.4', '::4.3.2.1/24');
 -- check that CIDR rejects invalid input:
 INSERT INTO INET_TBL (c, i) VALUES ('192.168.1.2/24', '192.168.1.226');
+INSERT INTO INET_TBL (c, i) VALUES ('1234::1234::1234', '::1.2.3.4');
 -- check that CIDR rejects invalid input when converting from text:
 INSERT INTO INET_TBL (c, i) VALUES (cidr('192.168.1.2/24'), '192.168.1.226');
-
+INSERT INTO INET_TBL (c, i) VALUES (cidr('ffff:ffff:ffff:ffff::/24'), '::192.168.1.226');
 SELECT '' AS ten, c AS cidr, i AS inet FROM INET_TBL;

 -- now test some support functions

-SELECT '' AS ten, i AS inet, host(i), text(i) FROM INET_TBL;
+SELECT '' AS ten, i AS inet, host(i), text(i), family(i) FROM INET_TBL;
 SELECT '' AS ten, c AS cidr, broadcast(c),
   i AS inet, broadcast(i) FROM INET_TBL;
 SELECT '' AS ten, c AS cidr, network(c) AS "network(cidr)",

Re: ipv6 patch #3

From
Bruce Momjian
Date:
Your patch has been added to the PostgreSQL unapplied patches list at:

    http://momjian.postgresql.org/cgi-bin/pgpatches

I will try to apply it within the next 48 hours.

---------------------------------------------------------------------------


Michael Graff wrote:
> This includes several revisions from others.
>
> Major changes:
>
>         Add ipv6 address parsing support to 'inet' and 'cidr' data types.
>
>         Regression tests for IPv6 operations added.
>
>         Documentation updated to document IPv6 bits.
>
>         Stop treating IPv4 as an "unsigned int" and IPv6 as an array of
>         characters.  Instead, always use the array of characters so we
>         can have one function fits all.  This makes bitncmp(), addressOK(),
>         and several other functions "just work" on both address families.
>
>         add family() function which returns integer 4 or 6 for IPv4 or
>         IPv6.  (See examples below)  Note that to add this new function
>         you will need to dump/initdb/reload or find the correct magic
>         to add the function to the postgresql function catalogs.
>
>         IPv4 addresses always sort before IPv6.
>
>         On disk we use AF_INET for IPv4, and AF_INET+1 for IPv6 addresses.
>         This prevents the need for a dump and reload, but lets IPv6 parsing
>         work on machines without AF_INET6.
>
>         To select all IPv4 addresses from a table:
>
>                 select * from foo where family(addr) = 4 ...
>
>         Order by and other bits should all work.
>
> Regression tests pass, at least on my NetBSD machine.
>
> --Michael
>
> diff -ur orig-postgresql-7.3.2/doc/src/sgml/datatype.sgml postgresql-7.3.2/doc/src/sgml/datatype.sgml
> --- orig-postgresql-7.3.2/doc/src/sgml/datatype.sgml    2003-01-28 17:09:03.000000000 -0800
> +++ postgresql-7.3.2/doc/src/sgml/datatype.sgml    2003-04-22 17:18:52.000000000 -0700
> @@ -101,7 +101,7 @@
>        <row>
>         <entry><type>cidr</type></entry>
>         <entry></entry>
> -       <entry>IP network address</entry>
> +       <entry>IPv4 or IPc6 network address</entry>
>        </row>
>
>        <row>
> @@ -125,7 +125,7 @@
>        <row>
>         <entry><type>inet</type></entry>
>         <entry></entry>
> -       <entry>IP host address</entry>
> +       <entry>IPv4 or IPv6 host address</entry>
>        </row>
>
>        <row>
> @@ -2538,7 +2538,7 @@
>     </indexterm>
>
>     <para>
> -    <productname>PostgreSQL</> offers data types to store IP and MAC
> +    <productname>PostgreSQL</> offers data types to store IPv4, IPv6, and MAC
>      addresses, shown in <xref linkend="datatype-net-types-table">.  It
>      is preferable to use these types over plain text types, because
>      these types offer input error checking and several specialized
> @@ -2560,16 +2560,16 @@
>
>         <row>
>      <entry><type>cidr</type></entry>
> -    <entry>12 bytes</entry>
> +    <entry>12 or 24 bytes</entry>
>      <entry>IP networks</entry>
> -    <entry>valid IPv4 networks</entry>
> +    <entry>valid IPv4 or IPv6 networks</entry>
>         </row>
>
>         <row>
>      <entry><type>inet</type></entry>
> -    <entry>12 bytes</entry>
> +    <entry>12 or 24 bytes</entry>
>      <entry>IP hosts and networks</entry>
> -    <entry>valid IPv4 hosts or networks</entry>
> +    <entry>valid IPv4 or IPv6 hosts or networks</entry>
>         </row>
>
>         <row>
> @@ -2584,7 +2584,10 @@
>      </table>
>
>     <para>
> -    IPv6 is not yet supported.
> +    When sorting <type>inet</type> or <type>cidr</type> data types,
> +    IPv4 addresses will always sort before IPv6 addresses, including
> +    IPv4 addresses encapsulated or mapped into IPv6 addresses, such as
> +    ::10.2.3.4 or ::ffff::10.4.3.2.
>     </para>
>
>
> @@ -2596,26 +2599,31 @@
>      </indexterm>
>
>      <para>
> -     The <type>inet</type> type holds an IP host address, and
> +     The <type>inet</type> type holds an IPv4 or IPv6 host address, and
>       optionally the identity of the subnet it is in, all in one field.
>       The subnet identity is represented by the number of bits in the
>       network part of the address (the <quote>netmask</quote>).  If the
> -     netmask is 32,
> -     then the value does not indicate a subnet, only a single host.
> +     netmask is 32 and the address is IPv4, then the value does not
> +     indicate a subnet, only a single host.  In IPv6, the address
> +     length is 128 bits.
>       Note that if you want to accept networks only, you should use the
>       <type>cidr</type> type rather than <type>inet</type>.
>      </para>
>
>      <para>
> -     The input format for this type is <replaceable
> -     class="parameter">x.x.x.x/y</replaceable> where <replaceable
> -     class="parameter">x.x.x.x</replaceable> is an IP address and
> -     <replaceable class="parameter">y</replaceable> is the number of
> -     bits in the netmask.  If the <replaceable
> -     class="parameter">/y</replaceable> part is left off, then the
> -     netmask is 32, and the value represents just a single host.
> -     On display, the <replaceable class="parameter">/y</replaceable>
> -     portion is suppressed if the netmask is 32.
> +     The input format for this type is
> +     <replaceable class="parameter">address/y</replaceable>
> +     where
> +     <replaceable class="parameter">address</replaceable>
> +     is an IPv4 or IPv6 address and
> +     <replaceable class="parameter">y</replaceable>
> +     is the number of bits in the netmask.  If the
> +     <replaceable class="parameter">/y</replaceable>
> +     part is left off, then the
> +     netmask is 32 for IPv4 and 128 for IPv6, and the value represents
> +     just a single host.  On display, the
> +     <replaceable class="parameter">/y</replaceable>
> +     portion is suppressed if the netmask specifies a single host.
>      </para>
>     </sect2>
>
> @@ -2627,13 +2635,14 @@
>      </indexterm>
>
>      <para>
> -     The <type>cidr</type> type holds an IP network specification.
> +     The <type>cidr</type> type holds an IPv4 or IPv6 network specification.
>       Input and output formats follow Classless Internet Domain Routing
>       conventions.
>       The format for
>       specifying classless networks is <replaceable
> -     class="parameter">x.x.x.x/y</> where <replaceable
> -     class="parameter">x.x.x.x</> is the network and <replaceable
> +     class="parameter">address/y</> where <replaceable
> +     class="parameter">address</> is the network represented as an
> +     IPv4 or IPv6 address, and <replaceable
>       class="parameter">y</> is the number of bits in the netmask.  If
>       <replaceable class="parameter">y</> is omitted, it is calculated
>       using assumptions from the older classful numbering system, except
> @@ -2711,6 +2720,28 @@
>       <entry>10.0.0.0/8</entry>
>       <entry>10/8</entry>
>      </row>
> +    <row>
> +     <entry>10.1.2.3/32</entry>
> +     <entry>10.1.2.3/32</entry>
> +         <entry>10.1.2.3/32</entry>
> +        <row>
> +     <entry>2001:4f8:3:ba::/64</entry>
> +     <entry>2001:4f8:3:ba::/64</entry>
> +     <entry>2001:4f8:3:ba::/64</entry>
> +    </row>
> +        <row>
> +     <entry>2001:4f8:3:ba:2e0:81ff:fe22:d1f1/128</entry>
> +     <entry>2001:4f8:3:ba:2e0:81ff:fe22:d1f1/128</entry>
> +     <entry>2001:4f8:3:ba:2e0:81ff:fe22:d1f1</entry>
> +    </row>
> +    <row>
> +     <entry>::ffff:1.2.3.0/120</entry>
> +     <entry>::ffff:1.2.3.0/120</entry>
> +         <entry>::ffff:1.2.3/120</entry>
> +    <row>
> +     <entry>::ffff:1.2.3.0/128</entry>
> +     <entry>::ffff:1.2.3.0/128</entry>
> +         <entry>::ffff:1.2.3.0/128</entry>
>         </tbody>
>        </tgroup>
>       </table>
> diff -ur orig-postgresql-7.3.2/doc/src/sgml/func.sgml postgresql-7.3.2/doc/src/sgml/func.sgml
> --- orig-postgresql-7.3.2/doc/src/sgml/func.sgml    2003-01-22 17:23:14.000000000 -0800
> +++ postgresql-7.3.2/doc/src/sgml/func.sgml    2003-04-22 17:20:23.000000000 -0700
> @@ -4886,6 +4886,11 @@
>      <entry><literal>inet '192.168.1.5' < inet '192.168.1.6'</literal></entry>
>         </row>
>         <row>
> +    <entry> < </entry>
> +    <entry>Less than</entry>
> +    <entry><literal>inet '1111::2222' < inet '2222::1111'</literal></entry>
> +       </row>
> +       <row>
>      <entry> <= </entry>
>      <entry>Less than or equal</entry>
>      <entry><literal>inet '192.168.1.5' <= inet '192.168.1.5'</literal></entry>
> diff -ur orig-postgresql-7.3.2/src/backend/utils/adt/inet_net_ntop.c
postgresql-7.3.2/src/backend/utils/adt/inet_net_ntop.c
> --- orig-postgresql-7.3.2/src/backend/utils/adt/inet_net_ntop.c    2002-09-01 19:47:04.000000000 -0700
> +++ postgresql-7.3.2/src/backend/utils/adt/inet_net_ntop.c    2003-04-22 17:12:05.000000000 -0700
> @@ -27,8 +27,12 @@
>
>  #include <errno.h>
>
> +#include "utils/inet.h"
>  #include "utils/builtins.h"
>
> +#define NS_IN6ADDRSZ 16
> +#define NS_INT16SZ 2
> +
>  #ifdef SPRINTF_CHAR
>  #define SPRINTF(x) strlen(sprintf/**/x)
>  #else
> @@ -36,9 +40,13 @@
>  #endif
>
>  static char *inet_net_ntop_ipv4(const u_char *src, int bits,
> -                   char *dst, size_t size);
> +                char *dst, size_t size);
>  static char *inet_cidr_ntop_ipv4(const u_char *src, int bits,
> -                    char *dst, size_t size);
> +                 char *dst, size_t size);
> +static char *inet_net_ntop_ipv6(const u_char *src, int bits,
> +                char *dst, size_t size);
> +static char *inet_cidr_ntop_ipv6(const u_char *src, int bits,
> +                 char *dst, size_t size);
>
>  /*
>   * char *
> @@ -55,8 +63,10 @@
>  {
>      switch (af)
>      {
> -        case AF_INET:
> +        case PGSQL_AF_INET:
>              return (inet_cidr_ntop_ipv4(src, bits, dst, size));
> +        case PGSQL_AF_INET6:
> +            return (inet_cidr_ntop_ipv6(src, bits, dst, size));
>          default:
>              errno = EAFNOSUPPORT;
>              return (NULL);
> @@ -136,6 +146,148 @@
>      return (NULL);
>  }
>
> +/*
> + * static char *
> + * inet_net_ntop_ipv6(src, bits, fakebits, dst, size)
> + *    convert IPv6 network number from network to presentation format.
> + *    generates CIDR style result always. Picks the shortest representation
> + *    unless the IP is really IPv4.
> + *    always prints specified number of bits (bits).
> + * return:
> + *    pointer to dst, or NULL if an error occurred (check errno).
> + * note:
> + *    network byte order assumed.  this means 192.5.5.240/28 has
> + *    0x11110000 in its fourth octet.
> + * author:
> + *    Vadim Kogan (UCB), June 2001
> + *  Original version (IPv4) by Paul Vixie (ISC), July 1996
> + */
> +
> +static char *
> +inet_cidr_ntop_ipv6(const u_char *src, int bits, char *dst, size_t size)
> +{
> +    u_int    m;
> +    int    b;
> +    int    p;
> +    int    zero_s, zero_l, tmp_zero_s, tmp_zero_l;
> +    int    i;
> +    int    is_ipv4 = 0;
> +    int     double_colon = 0;
> +    unsigned char inbuf[16];
> +    char outbuf[sizeof("xxxx:xxxx:xxxx:xxxx:xxxx:xxxx:255.255.255.255/128")];
> +    char    *cp;
> +    int    words;
> +    u_char    *s;
> +
> +    if (bits < 0 || bits > 128) {
> +        errno = EINVAL;
> +        return (NULL);
> +    }
> +
> +    cp = outbuf;
> +    double_colon = 0;
> +
> +    if (bits == 0) {
> +        *cp++ = ':';
> +        *cp++ = ':';
> +        *cp = '\0';
> +        double_colon = 1;
> +    } else {
> +        /* Copy src to private buffer.  Zero host part. */
> +        p = (bits + 7) / 8;
> +        memcpy(inbuf, src, p);
> +        memset(inbuf + p, 0, 16 - p);
> +        b = bits % 8;
> +        if (b != 0) {
> +            m = ~0 << (8 - b);
> +            inbuf[p-1] &= m;
> +        }
> +
> +        s = inbuf;
> +
> +        /* how many words need to be displayed in output */
> +        words = (bits + 15) / 16;
> +        if (words == 1)
> +            words = 2;
> +
> +        /* Find the longest substring of zero's */
> +        zero_s = zero_l = tmp_zero_s = tmp_zero_l = 0;
> +        for (i = 0; i < (words * 2); i += 2) {
> +            if ((s[i] | s[i+1]) == 0) {
> +                if (tmp_zero_l == 0)
> +                    tmp_zero_s = i / 2;
> +                tmp_zero_l++;
> +            } else {
> +                if (tmp_zero_l && zero_l < tmp_zero_l) {
> +                    zero_s = tmp_zero_s;
> +                    zero_l = tmp_zero_l;
> +                    tmp_zero_l = 0;
> +                }
> +            }
> +        }
> +
> +        if (tmp_zero_l && zero_l < tmp_zero_l) {
> +            zero_s = tmp_zero_s;
> +            zero_l = tmp_zero_l;
> +        }
> +
> +        if (zero_l != words && zero_s == 0 && ((zero_l == 6) ||
> +            ((zero_l == 5 && s[10] == 0xff && s[11] == 0xff) ||
> +            ((zero_l == 7 && s[14] != 0 && s[15] != 1)))))
> +            is_ipv4 = 1;
> +
> +        /* Format whole words. */
> +        for (p = 0; p < words; p++) {
> +            if (zero_l != 0 && p >= zero_s && p < zero_s + zero_l) {
> +                /* Time to skip some zeros */
> +                if (p == zero_s)
> +                    *cp++ = ':';
> +                if (p == words - 1) {
> +                    *cp++ = ':';
> +                    double_colon = 1;
> +                }
> +                s++;
> +                s++;
> +                continue;
> +            }
> +
> +            if (is_ipv4 && p > 5 ) {
> +                *cp++ = (p == 6) ? ':' : '.';
> +                cp += SPRINTF((cp, "%u", *s++));
> +                /* we can potentially drop the last octet */
> +                if (p != 7 || bits > 120) {
> +                    *cp++ = '.';
> +                    cp += SPRINTF((cp, "%u", *s++));
> +                }
> +            } else {
> +                if (cp != outbuf)
> +                    *cp++ = ':';
> +                cp += SPRINTF((cp, "%x", *s * 256 + s[1]));
> +                s += 2;
> +            }
> +        }
> +    }
> +
> +    if (!double_colon) {
> +        if (bits < 128 - 32)
> +            cp += SPRINTF((cp, "::", bits));
> +        else if (bits < 128 - 16)
> +            cp += SPRINTF((cp, ":0", bits));
> +    }
> +
> +    /* Format CIDR /width. */
> +    SPRINTF((cp, "/%u", bits));
> +
> +    if (strlen(outbuf) + 1 > size)
> +        goto emsgsize;
> +    strcpy(dst, outbuf);
> +
> +    return (dst);
> +
> +emsgsize:
> +    errno = EMSGSIZE;
> +    return (NULL);
> +}
>
>  /*
>   * char *
> @@ -156,8 +308,10 @@
>  {
>      switch (af)
>      {
> -        case AF_INET:
> +        case PGSQL_AF_INET:
>              return (inet_net_ntop_ipv4(src, bits, dst, size));
> +        case PGSQL_AF_INET6:
> +            return (inet_net_ntop_ipv6(src, bits, dst, size));
>          default:
>              errno = EAFNOSUPPORT;
>              return (NULL);
> @@ -217,3 +371,127 @@
>      errno = EMSGSIZE;
>      return (NULL);
>  }
> +
> +static int
> +decoct(const u_char *src, int bytes, char *dst, size_t size) {
> +    char *odst = dst;
> +    char *t;
> +    int b;
> +
> +    for (b = 1; b <= bytes; b++) {
> +        if (size < sizeof "255.")
> +            return (0);
> +        t = dst;
> +        dst += SPRINTF((dst, "%u", *src++));
> +        if (b != bytes) {
> +            *dst++ = '.';
> +            *dst = '\0';
> +        }
> +        size -= (size_t)(dst - t);
> +    }
> +    return (dst - odst);
> +}
> +
> +static char *
> +inet_net_ntop_ipv6(const u_char *src, int bits, char *dst, size_t size)
> +{
> +    /*
> +     * Note that int32_t and int16_t need only be "at least" large enough
> +     * to contain a value of the specified size.  On some systems, like
> +     * Crays, there is no such thing as an integer variable with 16 bits.
> +     * Keep this in mind if you think this function should have been coded
> +     * to use pointer overlays.  All the world's not a VAX.
> +     */
> +    char tmp[sizeof "ffff:ffff:ffff:ffff:ffff:ffff:255.255.255.255/128"];
> +    char *tp;
> +    struct { int base, len; } best, cur;
> +    u_int words[NS_IN6ADDRSZ / NS_INT16SZ];
> +    int i;
> +
> +    if ((bits < -1) || (bits > 128)) {
> +        errno = EINVAL;
> +        return (NULL);
> +    }
> +
> +    /*
> +     * Preprocess:
> +     *    Copy the input (bytewise) array into a wordwise array.
> +     *    Find the longest run of 0x00's in src[] for :: shorthanding.
> +     */
> +    memset(words, '\0', sizeof words);
> +    for (i = 0; i < NS_IN6ADDRSZ; i++)
> +        words[i / 2] |= (src[i] << ((1 - (i % 2)) << 3));
> +    best.base = -1;
> +    cur.base = -1;
> +    for (i = 0; i < (NS_IN6ADDRSZ / NS_INT16SZ); i++) {
> +        if (words[i] == 0) {
> +            if (cur.base == -1)
> +                cur.base = i, cur.len = 1;
> +            else
> +                cur.len++;
> +        } else {
> +            if (cur.base != -1) {
> +                if (best.base == -1 || cur.len > best.len)
> +                    best = cur;
> +                cur.base = -1;
> +            }
> +        }
> +    }
> +    if (cur.base != -1) {
> +        if (best.base == -1 || cur.len > best.len)
> +            best = cur;
> +    }
> +    if (best.base != -1 && best.len < 2)
> +        best.base = -1;
> +
> +    /*
> +     * Format the result.
> +     */
> +    tp = tmp;
> +    for (i = 0; i < (NS_IN6ADDRSZ / NS_INT16SZ); i++) {
> +        /* Are we inside the best run of 0x00's? */
> +        if (best.base != -1 && i >= best.base &&
> +            i < (best.base + best.len)) {
> +            if (i == best.base)
> +                *tp++ = ':';
> +            continue;
> +        }
> +        /* Are we following an initial run of 0x00s or any real hex? */
> +        if (i != 0)
> +            *tp++ = ':';
> +        /* Is this address an encapsulated IPv4? */
> +        if (i == 6 && best.base == 0 && (best.len == 6 ||
> +            (best.len == 7 && words[7] != 0x0001) ||
> +            (best.len == 5 && words[5] == 0xffff))) {
> +            int n;
> +
> +            n = decoct(src+12, 4, tp, sizeof tmp - (tp - tmp));
> +            if (n == 0) {
> +                errno = EMSGSIZE;
> +                return (NULL);
> +            }
> +            tp += strlen(tp);
> +            break;
> +        }
> +        tp += SPRINTF((tp, "%x", words[i]));
> +    }
> +
> +    /* Was it a trailing run of 0x00's? */
> +    if (best.base != -1 && (best.base + best.len) ==
> +        (NS_IN6ADDRSZ / NS_INT16SZ))
> +        *tp++ = ':';
> +    *tp = '\0';
> +
> +    if (bits != -1 && bits != 128)
> +        tp += SPRINTF((tp, "/%u", bits));
> +
> +    /*
> +     * Check for overflow, copy, and we're done.
> +     */
> +    if ((size_t)(tp - tmp) > size) {
> +        errno = EMSGSIZE;
> +        return (NULL);
> +    }
> +    strcpy(dst, tmp);
> +    return (dst);
> +}
> diff -ur orig-postgresql-7.3.2/src/backend/utils/adt/inet_net_pton.c
postgresql-7.3.2/src/backend/utils/adt/inet_net_pton.c
> --- orig-postgresql-7.3.2/src/backend/utils/adt/inet_net_pton.c    2002-09-01 19:47:04.000000000 -0700
> +++ postgresql-7.3.2/src/backend/utils/adt/inet_net_pton.c    2003-04-22 13:02:07.000000000 -0700
> @@ -29,16 +29,14 @@
>  #include <ctype.h>
>  #include <errno.h>
>
> -#include "utils/builtins.h"
> +#include "utils/inet.h"
>
> -#ifdef SPRINTF_CHAR
> -#define SPRINTF(x) strlen(sprintf/**/x)
> -#else
> -#define SPRINTF(x) ((size_t)sprintf x)
> -#endif
> +#include "utils/builtins.h"
>
>  static int    inet_net_pton_ipv4(const char *src, u_char *dst);
>  static int    inet_cidr_pton_ipv4(const char *src, u_char *dst, size_t size);
> +static int      inet_net_pton_ipv6(const char *src, u_char *dst);
> +static int      inet_cidr_pton_ipv6(const char *src, u_char *dst, size_t size);
>
>  /*
>   * static int
> @@ -63,10 +61,14 @@
>  {
>      switch (af)
>      {
> -        case AF_INET:
> +        case PGSQL_AF_INET:
>              return size == -1 ?
>                  inet_net_pton_ipv4(src, dst) :
>                  inet_cidr_pton_ipv4(src, dst, size);
> +        case PGSQL_AF_INET6:
> +            return size == -1 ?
> +                inet_net_pton_ipv6(src, dst) :
> +                inet_cidr_pton_ipv6(src, dst, size);
>          default:
>              errno = EAFNOSUPPORT;
>              return (-1);
> @@ -335,3 +337,199 @@
>      errno = EMSGSIZE;
>      return (-1);
>  }
> +
> +static int
> +getbits(const char *src, int *bitsp) {
> +    static const char digits[] = "0123456789";
> +    int n;
> +    int val;
> +    char ch;
> +
> +    val = 0;
> +    n = 0;
> +    while ((ch = *src++) != '\0') {
> +        const char *pch;
> +
> +        pch = strchr(digits, ch);
> +        if (pch != NULL) {
> +            if (n++ != 0 && val == 0)    /* no leading zeros */
> +                return (0);
> +            val *= 10;
> +            val += (pch - digits);
> +            if (val > 128)            /* range */
> +                return (0);
> +            continue;
> +        }
> +        return (0);
> +    }
> +    if (n == 0)
> +        return (0);
> +    *bitsp = val;
> +    return (1);
> +}
> +
> +static int
> +getv4(const char *src, u_char *dst, int *bitsp) {
> +    static const char digits[] = "0123456789";
> +    u_char *odst = dst;
> +    int n;
> +    u_int val;
> +    char ch;
> +
> +    val = 0;
> +    n = 0;
> +    while ((ch = *src++) != '\0') {
> +        const char *pch;
> +
> +        pch = strchr(digits, ch);
> +        if (pch != NULL) {
> +            if (n++ != 0 && val == 0)    /* no leading zeros */
> +                return (0);
> +            val *= 10;
> +            val += (pch - digits);
> +            if (val > 255)            /* range */
> +                return (0);
> +            continue;
> +        }
> +        if (ch == '.' || ch == '/') {
> +            if (dst - odst > 3)        /* too many octets? */
> +                return (0);
> +            *dst++ = val;
> +            if (ch == '/')
> +                return (getbits(src, bitsp));
> +            val = 0;
> +            n = 0;
> +            continue;
> +        }
> +        return (0);
> +    }
> +    if (n == 0)
> +        return (0);
> +    if (dst - odst > 3)        /* too many octets? */
> +        return (0);
> +    *dst++ = val;
> +    return (1);
> +}
> +
> +static int
> +inet_net_pton_ipv6(const char *src, u_char *dst)
> +{
> +    return inet_cidr_pton_ipv6(src, dst, 16);
> +}
> +
> +#define NS_IN6ADDRSZ 16
> +#define NS_INT16SZ 2
> +#define NS_INADDRSZ 4
> +
> +static int
> +inet_cidr_pton_ipv6(const char *src, u_char *dst, size_t size) {
> +    static const char xdigits_l[] = "0123456789abcdef",
> +              xdigits_u[] = "0123456789ABCDEF";
> +    u_char tmp[NS_IN6ADDRSZ], *tp, *endp, *colonp;
> +    const char *xdigits, *curtok;
> +    int ch, saw_xdigit;
> +    u_int val;
> +    int digits;
> +    int bits;
> +
> +    if (size < NS_IN6ADDRSZ)
> +        goto emsgsize;
> +
> +    memset((tp = tmp), '\0', NS_IN6ADDRSZ);
> +    endp = tp + NS_IN6ADDRSZ;
> +    colonp = NULL;
> +    /* Leading :: requires some special handling. */
> +    if (*src == ':')
> +        if (*++src != ':')
> +            goto enoent;
> +    curtok = src;
> +    saw_xdigit = 0;
> +    val = 0;
> +    digits = 0;
> +    bits = -1;
> +    while ((ch = *src++) != '\0') {
> +        const char *pch;
> +
> +        if ((pch = strchr((xdigits = xdigits_l), ch)) == NULL)
> +            pch = strchr((xdigits = xdigits_u), ch);
> +        if (pch != NULL) {
> +            val <<= 4;
> +            val |= (pch - xdigits);
> +            if (++digits > 4)
> +                goto enoent;
> +            saw_xdigit = 1;
> +            continue;
> +        }
> +        if (ch == ':') {
> +            curtok = src;
> +            if (!saw_xdigit) {
> +                if (colonp)
> +                    goto enoent;
> +                colonp = tp;
> +                continue;
> +            } else if (*src == '\0')
> +                goto enoent;
> +            if (tp + NS_INT16SZ > endp)
> +                return (0);
> +            *tp++ = (u_char) (val >> 8) & 0xff;
> +            *tp++ = (u_char) val & 0xff;
> +            saw_xdigit = 0;
> +            digits = 0;
> +            val = 0;
> +            continue;
> +        }
> +        if (ch == '.' && ((tp + NS_INADDRSZ) <= endp) &&
> +             getv4(curtok, tp, &bits) > 0) {
> +            tp += NS_INADDRSZ;
> +            saw_xdigit = 0;
> +            break;    /* '\0' was seen by inet_pton4(). */
> +        }
> +        if (ch == '/' && getbits(src, &bits) > 0)
> +            break;
> +        goto enoent;
> +    }
> +    if (saw_xdigit) {
> +        if (tp + NS_INT16SZ > endp)
> +            goto enoent;
> +        *tp++ = (u_char) (val >> 8) & 0xff;
> +        *tp++ = (u_char) val & 0xff;
> +    }
> +    if (bits == -1)
> +        bits = 128;
> +
> +    endp =  tmp + 16;
> +
> +    if (colonp != NULL) {
> +        /*
> +         * Since some memmove()'s erroneously fail to handle
> +         * overlapping regions, we'll do the shift by hand.
> +         */
> +        const int n = tp - colonp;
> +        int i;
> +
> +        if (tp == endp)
> +            goto enoent;
> +        for (i = 1; i <= n; i++) {
> +            endp[- i] = colonp[n - i];
> +            colonp[n - i] = 0;
> +        }
> +        tp = endp;
> +    }
> +    if (tp != endp)
> +        goto enoent;
> +
> +    /*
> +     * Copy out the result.
> +     */
> +    memcpy(dst, tmp, NS_IN6ADDRSZ);
> +
> +    return (bits);
> +
> + enoent:
> +    errno = ENOENT;
> +    return (-1);
> +
> + emsgsize:
> +    errno = EMSGSIZE;
> +    return (-1);
> +}
> diff -ur orig-postgresql-7.3.2/src/backend/utils/adt/network.c postgresql-7.3.2/src/backend/utils/adt/network.c
> --- orig-postgresql-7.3.2/src/backend/utils/adt/network.c    2002-09-01 19:47:04.000000000 -0700
> +++ postgresql-7.3.2/src/backend/utils/adt/network.c    2003-05-20 16:12:33.000000000 -0700
> @@ -1,7 +1,5 @@
>  /*
> - *    PostgreSQL type definitions for the INET type.    This
> - *    is for IP V4 CIDR notation, but prepared for V6: just
> - *    add the necessary bits where the comments indicate.
> + *    PostgreSQL type definitions for the INET and CIDR types.
>   *
>   *    $Header: /cvsroot/pgsql-server/src/backend/utils/adt/network.c,v 1.35 2002/09/02 02:47:04 momjian Exp $
>   *
> @@ -15,23 +13,22 @@
>  #include <netinet/in.h>
>  #include <arpa/inet.h>
>
> +#include <assert.h>
> +
>  #include "catalog/pg_type.h"
>  #include "utils/builtins.h"
>  #include "utils/inet.h"
>
> -
>  static Datum text_network(text *src, int type);
>  static int32 network_cmp_internal(inet *a1, inet *a2);
> -static int    v4bitncmp(unsigned long a1, unsigned long a2, int bits);
> -static bool v4addressOK(unsigned long a1, int bits);
> +static int bitncmp(void *l, void *r, int n);
> +static bool addressOK(unsigned char *a, int bits, int family);
> +static int ip_addrsize(inet *inetptr);
>
>  /*
> - *    Access macros.    Add IPV6 support.
> + *    Access macros.
>   */
>
> -#define ip_addrsize(inetptr) \
> -    (((inet_struct *)VARDATA(inetptr))->family == AF_INET ? 4 : -1)
> -
>  #define ip_family(inetptr) \
>      (((inet_struct *)VARDATA(inetptr))->family)
>
> @@ -41,43 +38,70 @@
>  #define ip_type(inetptr) \
>      (((inet_struct *)VARDATA(inetptr))->type)
>
> -#define ip_v4addr(inetptr) \
> -    (((inet_struct *)VARDATA(inetptr))->addr.ipv4_addr)
> +#define ip_addr(inetptr) \
> +    (((inet_struct *)VARDATA(inetptr))->ip_addr)
> +
> +#define ip_maxbits(inetptr) \
> +    (ip_family(inetptr) == PGSQL_AF_INET ? 32 : 128)
> +
> +/*
> + * Now, as a function!
> + * Return the number of bytes of storage needed for this data type.
> + */
> +static int
> +ip_addrsize(inet *inetptr)
> +{
> +    switch (ip_family(inetptr)) {
> +    case PGSQL_AF_INET:
> +        return 4;
> +    case PGSQL_AF_INET6:
> +        return 16;
> +    default:
> +        return -1;
> +    }
> +}
>
>  /* Common input routine */
>  static inet *
>  network_in(char *src, int type)
>  {
> -    int            bits;
> +    int        bits;
>      inet       *dst;
>
>      dst = (inet *) palloc(VARHDRSZ + sizeof(inet_struct));
>      /* make sure any unused bits in a CIDR value are zeroed */
>      MemSet(dst, 0, VARHDRSZ + sizeof(inet_struct));
>
> -    /* First, try for an IP V4 address: */
> -    ip_family(dst) = AF_INET;
> -    bits = inet_net_pton(ip_family(dst), src, &ip_v4addr(dst),
> -                         type ? ip_addrsize(dst) : -1);
> -    if ((bits < 0) || (bits > 32))
> -    {
> -        /* Go for an IPV6 address here, before faulting out: */
> -        elog(ERROR, "invalid %s value '%s'",
> -             type ? "CIDR" : "INET", src);
> +    /*
> +     * First, check to see if this is an IPv6 or IPv4 address.  IPv6
> +     * addresses will have a : somewhere in them (several, in fact) so
> +     * if there is one present, assume it's V6, otherwise assume it's V4.
> +     */
> +
> +    if (strchr(src, ':') != NULL) {
> +        ip_family(dst) = PGSQL_AF_INET6;
> +    } else {
> +        ip_family(dst) = PGSQL_AF_INET;
>      }
>
> +    bits = inet_net_pton(ip_family(dst), src, ip_addr(dst),
> +                 type ? ip_addrsize(dst) : -1);
> +    if ((bits < 0) || (bits > ip_maxbits(dst)))
> +        elog(ERROR, "invalid %s value '%s'",
> +             type ? "CIDR" : "INET", src);
> +
>      /*
> -     * Error check: CIDR values must not have any bits set beyond the
> -     * masklen. XXX this code is not IPV6 ready.
> +     * Error check: CIDR values must not have any bits set beyond
> +     * the masklen.
>       */
>      if (type)
>      {
> -        if (!v4addressOK(ip_v4addr(dst), bits))
> +        if (!addressOK(ip_addr(dst), bits, ip_family(dst)))
>              elog(ERROR, "invalid CIDR value '%s': has bits set to right of mask", src);
>      }
>
>      VARATT_SIZEP(dst) = VARHDRSZ
> -        + ((char *) &ip_v4addr(dst) - (char *) VARDATA(dst))
> +        + ((char *) &ip_addr(dst) - (char *) VARDATA(dst))
>          + ip_addrsize(dst);
>      ip_bits(dst) = bits;
>      ip_type(dst) = type;
> @@ -110,32 +134,20 @@
>  inet_out(PG_FUNCTION_ARGS)
>  {
>      inet       *src = PG_GETARG_INET_P(0);
> -    char        tmp[sizeof("255.255.255.255/32")];
> +    char        tmp[sizeof("xxxx:xxxx:xxxx:xxxx:xxxx:xxxx:255.255.255.255/128")];
>      char       *dst;
>      int            len;
>
> -    if (ip_family(src) == AF_INET)
> +    dst = inet_net_ntop(ip_family(src), ip_addr(src), ip_bits(src),
> +                tmp, sizeof(tmp));
> +    if (dst == NULL)
> +        elog(ERROR, "unable to print address (%s)", strerror(errno));
> +    /* For CIDR, add /n if not present */
> +    if (ip_type(src) && strchr(tmp, '/') == NULL)
>      {
> -        /* It's an IP V4 address: */
> -
> -        /*
> -         * Use inet style for both inet and cidr, since we don't want
> -         * abbreviated CIDR style here.
> -         */
> -        dst = inet_net_ntop(AF_INET, &ip_v4addr(src), ip_bits(src),
> -                            tmp, sizeof(tmp));
> -        if (dst == NULL)
> -            elog(ERROR, "unable to print address (%s)", strerror(errno));
> -        /* For CIDR, add /n if not present */
> -        if (ip_type(src) && strchr(tmp, '/') == NULL)
> -        {
> -            len = strlen(tmp);
> -            snprintf(tmp + len, sizeof(tmp) - len, "/%u", ip_bits(src));
> -        }
> +        len = strlen(tmp);
> +        snprintf(tmp + len, sizeof(tmp) - len, "/%u", ip_bits(src));
>      }
> -    else
> -        /* Go for an IPV6 address here, before faulting out: */
> -        elog(ERROR, "unknown address family (%d)", ip_family(src));
>
>      PG_RETURN_CSTRING(pstrdup(tmp));
>  }
> @@ -162,6 +174,7 @@
>      PG_RETURN_INET_P(network_in(str, type));
>  }
>
> +
>  Datum
>  text_cidr(PG_FUNCTION_ARGS)
>  {
> @@ -181,8 +194,11 @@
>      int            bits = PG_GETARG_INT32(1);
>      inet       *dst;
>
> -    if ((bits < 0) || (bits > 32))        /* no support for v6 yet */
> -        elog(ERROR, "set_masklen - invalid value '%d'", bits);
> +        if ( bits == -1 )
> +            bits = ip_maxbits(src);
> +
> +    if ((bits < 0) || (bits > ip_maxbits(src)))
> +            elog(ERROR, "set_masklen - invalid value '%d'", bits);
>
>      /* clone the original data */
>      dst = (inet *) palloc(VARHDRSZ + sizeof(inet_struct));
> @@ -207,26 +223,21 @@
>  static int32
>  network_cmp_internal(inet *a1, inet *a2)
>  {
> -    if (ip_family(a1) == AF_INET && ip_family(a2) == AF_INET)
> +    if (ip_family(a1) == ip_family(a2))
>      {
>          int            order;
>
> -        order = v4bitncmp(ip_v4addr(a1), ip_v4addr(a2),
> -                          Min(ip_bits(a1), ip_bits(a2)));
> +        order = bitncmp(ip_addr(a1), ip_addr(a2),
> +                Min(ip_bits(a1), ip_bits(a2)));
>          if (order != 0)
>              return order;
>          order = ((int) ip_bits(a1)) - ((int) ip_bits(a2));
>          if (order != 0)
>              return order;
> -        return v4bitncmp(ip_v4addr(a1), ip_v4addr(a2), 32);
> -    }
> -    else
> -    {
> -        /* Go for an IPV6 address here, before faulting out: */
> -        elog(ERROR, "cannot compare address families %d and %d",
> -             ip_family(a1), ip_family(a2));
> -        return 0;                /* keep compiler quiet */
> +        return bitncmp(ip_addr(a1), ip_addr(a2), ip_maxbits(a1));
>      }
> +
> +    return ip_family(a1) - ip_family(a2);
>  }
>
>  Datum
> @@ -304,18 +315,13 @@
>      inet       *a1 = PG_GETARG_INET_P(0);
>      inet       *a2 = PG_GETARG_INET_P(1);
>
> -    if ((ip_family(a1) == AF_INET) && (ip_family(a2) == AF_INET))
> +    if (ip_family(a1) == ip_family(a2))
>      {
>          PG_RETURN_BOOL(ip_bits(a1) > ip_bits(a2)
> -           && v4bitncmp(ip_v4addr(a1), ip_v4addr(a2), ip_bits(a2)) == 0);
> -    }
> -    else
> -    {
> -        /* Go for an IPV6 address here, before faulting out: */
> -        elog(ERROR, "cannot compare address families %d and %d",
> -             ip_family(a1), ip_family(a2));
> -        PG_RETURN_BOOL(false);
> +           && bitncmp(ip_addr(a1), ip_addr(a2), ip_bits(a2)) == 0);
>      }
> +
> +    PG_RETURN_BOOL(false);
>  }
>
>  Datum
> @@ -324,18 +330,13 @@
>      inet       *a1 = PG_GETARG_INET_P(0);
>      inet       *a2 = PG_GETARG_INET_P(1);
>
> -    if ((ip_family(a1) == AF_INET) && (ip_family(a2) == AF_INET))
> +    if (ip_family(a1) == ip_family(a2))
>      {
>          PG_RETURN_BOOL(ip_bits(a1) >= ip_bits(a2)
> -           && v4bitncmp(ip_v4addr(a1), ip_v4addr(a2), ip_bits(a2)) == 0);
> -    }
> -    else
> -    {
> -        /* Go for an IPV6 address here, before faulting out: */
> -        elog(ERROR, "cannot compare address families %d and %d",
> -             ip_family(a1), ip_family(a2));
> -        PG_RETURN_BOOL(false);
> +           && bitncmp(ip_addr(a1), ip_addr(a2), ip_bits(a2)) == 0);
>      }
> +
> +    PG_RETURN_BOOL(false);
>  }
>
>  Datum
> @@ -344,18 +345,13 @@
>      inet       *a1 = PG_GETARG_INET_P(0);
>      inet       *a2 = PG_GETARG_INET_P(1);
>
> -    if ((ip_family(a1) == AF_INET) && (ip_family(a2) == AF_INET))
> +    if (ip_family(a1) == ip_family(a2))
>      {
>          PG_RETURN_BOOL(ip_bits(a1) < ip_bits(a2)
> -           && v4bitncmp(ip_v4addr(a1), ip_v4addr(a2), ip_bits(a1)) == 0);
> -    }
> -    else
> -    {
> -        /* Go for an IPV6 address here, before faulting out: */
> -        elog(ERROR, "cannot compare address families %d and %d",
> -             ip_family(a1), ip_family(a2));
> -        PG_RETURN_BOOL(false);
> +           && bitncmp(ip_addr(a1), ip_addr(a2), ip_bits(a1)) == 0);
>      }
> +
> +    PG_RETURN_BOOL(false);
>  }
>
>  Datum
> @@ -364,18 +360,13 @@
>      inet       *a1 = PG_GETARG_INET_P(0);
>      inet       *a2 = PG_GETARG_INET_P(1);
>
> -    if ((ip_family(a1) == AF_INET) && (ip_family(a2) == AF_INET))
> +    if (ip_family(a1) == ip_family(a2))
>      {
>          PG_RETURN_BOOL(ip_bits(a1) <= ip_bits(a2)
> -           && v4bitncmp(ip_v4addr(a1), ip_v4addr(a2), ip_bits(a1)) == 0);
> -    }
> -    else
> -    {
> -        /* Go for an IPV6 address here, before faulting out: */
> -        elog(ERROR, "cannot compare address families %d and %d",
> -             ip_family(a1), ip_family(a2));
> -        PG_RETURN_BOOL(false);
> +           && bitncmp(ip_addr(a1), ip_addr(a2), ip_bits(a1)) == 0);
>      }
> +
> +    PG_RETURN_BOOL(false);
>  }
>
>  /*
> @@ -387,19 +378,13 @@
>      inet       *ip = PG_GETARG_INET_P(0);
>      text       *ret;
>      int            len;
> -    char       *ptr,
> -                tmp[sizeof("255.255.255.255/32")];
> +    char       *ptr;
> +    char        tmp[sizeof("xxxx:xxxx:xxxx:xxxx:xxxx:xxxx:255.255.255.255/128")];
>
> -    if (ip_family(ip) == AF_INET)
> -    {
> -        /* It's an IP V4 address: */
> -        /* force display of 32 bits, regardless of masklen... */
> -        if (inet_net_ntop(AF_INET, &ip_v4addr(ip), 32, tmp, sizeof(tmp)) == NULL)
> -            elog(ERROR, "unable to print host (%s)", strerror(errno));
> -    }
> -    else
> -        /* Go for an IPV6 address here, before faulting out: */
> -        elog(ERROR, "unknown address family (%d)", ip_family(ip));
> +    /* force display of max bits, regardless of masklen... */
> +    if (inet_net_ntop(ip_family(ip), ip_addr(ip), ip_maxbits(ip),
> +              tmp, sizeof(tmp)) == NULL)
> +        elog(ERROR, "unable to print host (%s)", strerror(errno));
>
>      /* Suppress /n if present (shouldn't happen now) */
>      if ((ptr = strchr(tmp, '/')) != NULL)
> @@ -419,24 +404,17 @@
>      inet       *ip = PG_GETARG_INET_P(0);
>      text       *ret;
>      int            len;
> -    char        tmp[sizeof("255.255.255.255/32")];
> +    char        tmp[sizeof("xxxx:xxxx:xxxx:xxxx:xxxx:xxxx:255.255.255.255/128")];
>
> -    if (ip_family(ip) == AF_INET)
> +    if (inet_net_ntop(ip_family(ip), ip_addr(ip), ip_maxbits(ip),
> +              tmp, sizeof(tmp)) == NULL)
> +        elog(ERROR, "unable to print host (%s)", strerror(errno));
> +    /* Add /n if not present (which it won't be) */
> +    if (strchr(tmp, '/') == NULL)
>      {
> -        /* It's an IP V4 address: */
> -        /* force display of 32 bits, regardless of masklen... */
> -        if (inet_net_ntop(AF_INET, &ip_v4addr(ip), 32, tmp, sizeof(tmp)) == NULL)
> -            elog(ERROR, "unable to print host (%s)", strerror(errno));
> -        /* Add /n if not present (which it won't be) */
> -        if (strchr(tmp, '/') == NULL)
> -        {
> -            len = strlen(tmp);
> -            snprintf(tmp + len, sizeof(tmp) - len, "/%u", ip_bits(ip));
> -        }
> +        len = strlen(tmp);
> +        snprintf(tmp + len, sizeof(tmp) - len, "/%u", ip_bits(ip));
>      }
> -    else
> -        /* Go for an IPV6 address here, before faulting out: */
> -        elog(ERROR, "unknown address family (%d)", ip_family(ip));
>
>      /* Return string as a text datum */
>      len = strlen(tmp);
> @@ -453,24 +431,18 @@
>      text       *ret;
>      char       *dst;
>      int            len;
> -    char        tmp[sizeof("255.255.255.255/32")];
> -
> -    if (ip_family(ip) == AF_INET)
> -    {
> -        /* It's an IP V4 address: */
> -        if (ip_type(ip))
> -            dst = inet_cidr_ntop(AF_INET, &ip_v4addr(ip), ip_bits(ip),
> -                                 tmp, sizeof(tmp));
> -        else
> -            dst = inet_net_ntop(AF_INET, &ip_v4addr(ip), ip_bits(ip),
> -                                tmp, sizeof(tmp));
> +    char        tmp[sizeof("xxxx:xxxx:xxxx:xxxx:xxxx:xxxx:255.255.255.255/128")];
>
> -        if (dst == NULL)
> -            elog(ERROR, "unable to print address (%s)", strerror(errno));
> -    }
> -    else
> -        /* Go for an IPV6 address here, before faulting out: */
> -        elog(ERROR, "unknown address family (%d)", ip_family(ip));
> +    if (ip_type(ip))
> +        dst = inet_cidr_ntop(ip_family(ip), ip_addr(ip),
> +                     ip_bits(ip), tmp, sizeof(tmp));
> +    else
> +        dst = inet_net_ntop(ip_family(ip), ip_addr(ip),
> +                    ip_bits(ip), tmp, sizeof(tmp));
> +
> +    if (dst == NULL)
> +        elog(ERROR, "unable to print address (%s)",
> +             strerror(errno));
>
>      /* Return string as a text datum */
>      len = strlen(tmp);
> @@ -489,40 +461,67 @@
>  }
>
>  Datum
> +network_family(PG_FUNCTION_ARGS)
> +{
> +    inet       *ip = PG_GETARG_INET_P(0);
> +
> +    switch (ip_family(ip)) {
> +    case PGSQL_AF_INET:
> +        PG_RETURN_INT32(4);
> +        break;
> +    case PGSQL_AF_INET6:
> +        PG_RETURN_INT32(6);
> +        break;
> +    default:
> +        PG_RETURN_INT32(0);
> +        break;
> +    }
> +}
> +
> +Datum
>  network_broadcast(PG_FUNCTION_ARGS)
>  {
>      inet       *ip = PG_GETARG_INET_P(0);
>      inet       *dst;
> +    int byte;
> +    int bits;
> +    int maxbytes;
> +    unsigned char mask;
> +    unsigned char *a, *b;
>
>      dst = (inet *) palloc(VARHDRSZ + sizeof(inet_struct));
>      /* make sure any unused bits are zeroed */
>      MemSet(dst, 0, VARHDRSZ + sizeof(inet_struct));
>
> -    if (ip_family(ip) == AF_INET)
> -    {
> -        /* It's an IP V4 address: */
> -        unsigned long mask = 0xffffffff;
> -
> -        /*
> -         * Shifting by 32 or more bits does not yield portable results, so
> -         * don't try it.
> -         */
> -        if (ip_bits(ip) < 32)
> -            mask >>= ip_bits(ip);
> -        else
> -            mask = 0;
> -
> -        ip_v4addr(dst) = htonl(ntohl(ip_v4addr(ip)) | mask);
> +    if (ip_family(ip) == PGSQL_AF_INET) {
> +        maxbytes = 4;
> +    } else {
> +        maxbytes = 16;
> +    }
> +
> +    bits = ip_bits(ip);
> +    a = ip_addr(ip);
> +    b = ip_addr(dst);
> +
> +    for (byte = 0 ; byte < maxbytes ; byte++) {
> +        if (bits >= 8) {
> +            mask = 0x00;
> +            bits -= 8;
> +        } else if (bits == 0) {
> +            mask = 0xff;
> +        } else {
> +            mask = 0xff >> bits;
> +            bits = 0;
> +        }
> +
> +        b[byte] = a[byte] | mask;
>      }
> -    else
> -        /* Go for an IPV6 address here, before faulting out: */
> -        elog(ERROR, "unknown address family (%d)", ip_family(ip));
>
>      ip_family(dst) = ip_family(ip);
>      ip_bits(dst) = ip_bits(ip);
>      ip_type(dst) = 0;
>      VARATT_SIZEP(dst) = VARHDRSZ
> -        + ((char *) &ip_v4addr(dst) - (char *) VARDATA(dst))
> +        + ((char *)ip_addr(dst) - (char *)VARDATA(dst))
>          + ip_addrsize(dst);
>
>      PG_RETURN_INET_P(dst);
> @@ -533,36 +532,45 @@
>  {
>      inet       *ip = PG_GETARG_INET_P(0);
>      inet       *dst;
> +    int byte;
> +    int bits;
> +    int maxbytes;
> +    unsigned char mask;
> +    unsigned char *a, *b;
>
>      dst = (inet *) palloc(VARHDRSZ + sizeof(inet_struct));
>      /* make sure any unused bits are zeroed */
>      MemSet(dst, 0, VARHDRSZ + sizeof(inet_struct));
>
> -    if (ip_family(ip) == AF_INET)
> -    {
> -        /* It's an IP V4 address: */
> -        unsigned long mask = 0xffffffff;
> -
> -        /*
> -         * Shifting by 32 or more bits does not yield portable results, so
> -         * don't try it.
> -         */
> -        if (ip_bits(ip) > 0)
> -            mask <<= (32 - ip_bits(ip));
> -        else
> -            mask = 0;
> -
> -        ip_v4addr(dst) = htonl(ntohl(ip_v4addr(ip)) & mask);
> +    if (ip_family(ip) == PGSQL_AF_INET) {
> +        maxbytes = 4;
> +    } else {
> +        maxbytes = 16;
> +    }
> +
> +    bits = ip_bits(ip);
> +    a = ip_addr(ip);
> +    b = ip_addr(dst);
> +
> +    byte = 0;
> +    while (bits) {
> +        if (bits >= 8) {
> +            mask = 0xff;
> +            bits -= 8;
> +        } else {
> +            mask = 0xff << (8 - bits);
> +            bits = 0;
> +        }
> +
> +        b[byte] = a[byte] & mask;
> +        byte++;
>      }
> -    else
> -        /* Go for an IPV6 address here, before faulting out: */
> -        elog(ERROR, "unknown address family (%d)", ip_family(ip));
>
>      ip_family(dst) = ip_family(ip);
>      ip_bits(dst) = ip_bits(ip);
>      ip_type(dst) = 1;
>      VARATT_SIZEP(dst) = VARHDRSZ
> -        + ((char *) &ip_v4addr(dst) - (char *) VARDATA(dst))
> +        + ((char *)ip_addr(dst) - (char *)VARDATA(dst))
>          + ip_addrsize(dst);
>
>      PG_RETURN_INET_P(dst);
> @@ -573,37 +581,44 @@
>  {
>      inet       *ip = PG_GETARG_INET_P(0);
>      inet       *dst;
> +    int byte;
> +    int bits;
> +    int maxbytes;
> +    unsigned char mask;
> +    unsigned char *b;
>
>      dst = (inet *) palloc(VARHDRSZ + sizeof(inet_struct));
>      /* make sure any unused bits are zeroed */
>      MemSet(dst, 0, VARHDRSZ + sizeof(inet_struct));
>
> -    if (ip_family(ip) == AF_INET)
> -    {
> -        /* It's an IP V4 address: */
> -        unsigned long mask = 0xffffffff;
> -
> -        /*
> -         * Shifting by 32 or more bits does not yield portable results, so
> -         * don't try it.
> -         */
> -        if (ip_bits(ip) > 0)
> -            mask <<= (32 - ip_bits(ip));
> -        else
> -            mask = 0;
> -
> -        ip_v4addr(dst) = htonl(mask);
> -
> -        ip_bits(dst) = 32;
> +    if (ip_family(ip) == PGSQL_AF_INET) {
> +        maxbytes = 4;
> +    } else {
> +        maxbytes = 16;
> +    }
> +
> +    bits = ip_bits(ip);
> +    b = ip_addr(dst);
> +
> +    byte = 0;
> +    while (bits) {
> +        if (bits >= 8) {
> +            mask = 0xff;
> +            bits -= 8;
> +        } else {
> +            mask = 0xff << (8 - bits);
> +            bits = 0;
> +        }
> +
> +        b[byte] = mask;
> +        byte++;
>      }
> -    else
> -        /* Go for an IPV6 address here, before faulting out: */
> -        elog(ERROR, "unknown address family (%d)", ip_family(ip));
>
>      ip_family(dst) = ip_family(ip);
> +    ip_bits(dst) = ip_bits(ip);
>      ip_type(dst) = 0;
>      VARATT_SIZEP(dst) = VARHDRSZ
> -        + ((char *) &ip_v4addr(dst) - (char *) VARDATA(dst))
> +        + ((char *)ip_addr(dst) - (char *)VARDATA(dst))
>          + ip_addrsize(dst);
>
>      PG_RETURN_INET_P(dst);
> @@ -629,12 +644,26 @@
>          case CIDROID:
>              {
>                  inet       *ip = DatumGetInetP(value);
> -
> -                if (ip_family(ip) == AF_INET)
> -                    return (double) ip_v4addr(ip);
> +                int len;
> +                double res;
> +                int i;
> +
> +                /*
> +                 * Note that we don't use the full address
> +                 * here.
> +                 */
> +                if (ip_family(ip) == PGSQL_AF_INET)
> +                    len = 4;
>                  else
> -                    /* Go for an IPV6 address here, before faulting out: */
> -                    elog(ERROR, "unknown address family (%d)", ip_family(ip));
> +                    len = 5;
> +
> +                res = ip_family(ip);
> +                for (i = 0 ; i < len ; i++) {
> +                    res *= 256;
> +                    res += ip_addr(ip)[i];
> +                }
> +                return res;
> +
>                  break;
>              }
>          case MACADDROID:
> @@ -657,53 +686,78 @@
>      return 0;
>  }
>
> -
>  /*
> - *    Bitwise comparison for V4 addresses.  Add V6 implementation!
> + * int
> + * bitncmp(l, r, n)
> + *      compare bit masks l and r, for n bits.
> + * return:
> + *      -1, 1, or 0 in the libc tradition.
> + * note:
> + *      network byte order assumed.  this means 192.5.5.240/28 has
> + *      0x11110000 in its fourth octet.
> + * author:
> + *      Paul Vixie (ISC), June 1996
>   */
> -
>  static int
> -v4bitncmp(unsigned long a1, unsigned long a2, int bits)
> +bitncmp(void *l, void *r, int n)
>  {
> -    unsigned long mask;
> +    u_int lb, rb;
> +    int x, b;
>
> -    /*
> -     * Shifting by 32 or more bits does not yield portable results, so
> -     * don't try it.
> -     */
> -    if (bits > 0)
> -        mask = (0xFFFFFFFFL << (32 - bits)) & 0xFFFFFFFFL;
> -    else
> -        mask = 0;
> -    a1 = ntohl(a1);
> -    a2 = ntohl(a2);
> -    if ((a1 & mask) < (a2 & mask))
> -        return (-1);
> -    else if ((a1 & mask) > (a2 & mask))
> -        return (1);
> +    b = n / 8;
> +    x = memcmp(l, r, b);
> +    if (x)
> +        return (x);
> +
> +    lb = ((const u_char *)l)[b];
> +    rb = ((const u_char *)r)[b];
> +    for (b = n % 8; b > 0; b--) {
> +        if ((lb & 0x80) != (rb & 0x80)) {
> +            if (lb & 0x80)
> +                return (1);
> +            return (-1);
> +        }
> +        lb <<= 1;
> +        rb <<= 1;
> +    }
>      return (0);
>  }
>
> -/*
> - * Returns true if given address fits fully within the specified bit width.
> - */
>  static bool
> -v4addressOK(unsigned long a1, int bits)
> +addressOK(unsigned char *a, int bits, int family)
>  {
> -    unsigned long mask;
> +    int byte;
> +    int nbits;
> +    int maxbits;
> +    int maxbytes;
> +    unsigned char mask;
> +
> +    if (family == PGSQL_AF_INET) {
> +        maxbits = 32;
> +        maxbytes = 4;
> +    } else {
> +        maxbits = 128;
> +        maxbytes = 16;
> +    }
> +    assert(bits <= maxbits);
> +
> +    if (bits == maxbits)
> +        return 1;
> +
> +    byte = (bits + 7) / 8;
> +    nbits = bits % 8;
> +    mask = 0xff;
> +    if (bits != 0)
> +        mask >>= nbits;
> +
> +    while (byte < maxbytes) {
> +        if ((a[byte] & mask) != 0)
> +            return 0;
> +        mask = 0xff;
> +        byte++;
> +    }
>
> -    /*
> -     * Shifting by 32 or more bits does not yield portable results, so
> -     * don't try it.
> -     */
> -    if (bits > 0)
> -        mask = (0xFFFFFFFFL << (32 - bits)) & 0xFFFFFFFFL;
> -    else
> -        mask = 0;
> -    a1 = ntohl(a1);
> -    if ((a1 & mask) == a1)
> -        return true;
> -    return false;
> +    return 1;
>  }
>
>
> @@ -721,15 +775,16 @@
>
>  /*
>   * return "last" IP on a given network. It's the broadcast address,
> - * however, masklen has to be set to 32, since
> + * however, masklen has to be set to its max btis, since
>   * 192.168.0.255/24 is considered less than 192.168.0.255/32
>   *
> - * NB: this is not IPv6 ready ...
> + * inet_set_masklen() hacked to max out the masklength to 128 for IPv6
> + * and 32 for IPv4 when given '-1' as argument.
>   */
>  Datum
>  network_scan_last(Datum in)
>  {
>      return DirectFunctionCall2(inet_set_masklen,
>                                 DirectFunctionCall1(network_broadcast, in),
> -                               Int32GetDatum(32));
> +               Int32GetDatum(-1));
>  }
> diff -ur orig-postgresql-7.3.2/src/include/catalog/pg_proc.h postgresql-7.3.2/src/include/catalog/pg_proc.h
> --- orig-postgresql-7.3.2/src/include/catalog/pg_proc.h    2002-11-02 10:41:22.000000000 -0800
> +++ postgresql-7.3.2/src/include/catalog/pg_proc.h    2003-04-22 13:02:07.000000000 -0700
> @@ -2353,6 +2353,8 @@
>  DESCR("show address octets only");
>  DATA(insert OID = 730 (  text                PGNSP PGUID 12 f f t f i 1 25 "869"  network_show - _null_ ));
>  DESCR("show all parts of inet/cidr value");
> +DATA(insert OID = 731 (  family                PGNSP PGUID 12 f f t f i 1 23 "869"  network_family - _null_ ));
> +DESCR("return address family (4 for IPv4, 6 for IPv6)");
>  DATA(insert OID = 1713 (  inet                PGNSP PGUID 12 f f t f i 1 869 "25"  text_inet - _null_ ));
>  DESCR("text to inet");
>  DATA(insert OID = 1714 (  cidr                PGNSP PGUID 12 f f t f i 1 650 "25"  text_cidr - _null_ ));
> diff -ur orig-postgresql-7.3.2/src/include/utils/builtins.h postgresql-7.3.2/src/include/utils/builtins.h
> --- orig-postgresql-7.3.2/src/include/utils/builtins.h    2002-11-02 10:41:22.000000000 -0800
> +++ postgresql-7.3.2/src/include/utils/builtins.h    2003-04-22 13:02:07.000000000 -0700
> @@ -566,6 +566,7 @@
>  extern Datum network_network(PG_FUNCTION_ARGS);
>  extern Datum network_netmask(PG_FUNCTION_ARGS);
>  extern Datum network_masklen(PG_FUNCTION_ARGS);
> +extern Datum network_family(PG_FUNCTION_ARGS);
>  extern Datum network_broadcast(PG_FUNCTION_ARGS);
>  extern Datum network_host(PG_FUNCTION_ARGS);
>  extern Datum network_show(PG_FUNCTION_ARGS);
> diff -ur orig-postgresql-7.3.2/src/include/utils/inet.h postgresql-7.3.2/src/include/utils/inet.h
> --- orig-postgresql-7.3.2/src/include/utils/inet.h    2002-06-20 13:29:53.000000000 -0700
> +++ postgresql-7.3.2/src/include/utils/inet.h    2003-04-22 13:02:07.000000000 -0700
> @@ -23,14 +23,20 @@
>      unsigned char family;
>      unsigned char bits;
>      unsigned char type;
> -    union
> -    {
> -        unsigned int ipv4_addr; /* network byte order */
> -        /* add IPV6 address type here */
> -    }            addr;
> +    unsigned char ip_addr[16]; /* 128 bits of address */
>  } inet_struct;
>
>  /*
> + * Referencing all of the non-AF_INET types to AF_INET lets us work on
> + * machines which may not have the appropriate address family (like
> + * inet6 addresses when AF_INET6 isn't present) but doesn't cause a
> + * dump/reload requirement.  Existing databases used AF_INET for the family
> + * type on disk.
> + */
> +#define PGSQL_AF_INET    (AF_INET + 0)
> +#define PGSQL_AF_INET6    (AF_INET + 1)
> +
> +/*
>   * Both INET and CIDR addresses are represented within Postgres as varlena
>   * objects, ie, there is a varlena header (basically a length word) in front
>   * of the struct type depicted above.
> diff -ur orig-postgresql-7.3.2/src/test/regress/expected/inet.out postgresql-7.3.2/src/test/regress/expected/inet.out
> --- orig-postgresql-7.3.2/src/test/regress/expected/inet.out    2001-06-16 19:05:20.000000000 -0700
> +++ postgresql-7.3.2/src/test/regress/expected/inet.out    2003-04-22 16:48:02.000000000 -0700
> @@ -19,110 +19,132 @@
>  INSERT INTO INET_TBL (c, i) VALUES ('10', '10.1.2.3/8');
>  INSERT INTO INET_TBL (c, i) VALUES ('10', '11.1.2.3/8');
>  INSERT INTO INET_TBL (c, i) VALUES ('10', '9.1.2.3/8');
> +INSERT INTO INET_TBL (c, i) VALUES ('10:23::f1', '10:23::f1/64');
> +INSERT INTO INET_TBL (c, i) VALUES ('10:23::8000/113', '10:23::ffff');
> +INSERT INTO INET_TBL (c, i) VALUES ('::ffff:1.2.3.4', '::4.3.2.1/24');
>  -- check that CIDR rejects invalid input:
>  INSERT INTO INET_TBL (c, i) VALUES ('192.168.1.2/24', '192.168.1.226');
>  ERROR:  invalid CIDR value '192.168.1.2/24': has bits set to right of mask
> +INSERT INTO INET_TBL (c, i) VALUES ('1234::1234::1234', '::1.2.3.4');
> +ERROR:  invalid CIDR value '1234::1234::1234'
>  -- check that CIDR rejects invalid input when converting from text:
>  INSERT INTO INET_TBL (c, i) VALUES (cidr('192.168.1.2/24'), '192.168.1.226');
>  ERROR:  invalid CIDR value '192.168.1.2/24': has bits set to right of mask
> +INSERT INTO INET_TBL (c, i) VALUES (cidr('ffff:ffff:ffff:ffff::/24'), '::192.168.1.226');
> +ERROR:  invalid CIDR value 'ffff:ffff:ffff:ffff::/24': has bits set to right of mask
>  SELECT '' AS ten, c AS cidr, i AS inet FROM INET_TBL;
> - ten |      cidr      |       inet
> ------+----------------+------------------
> -     | 192.168.1.0/24 | 192.168.1.226/24
> -     | 192.168.1.0/24 | 192.168.1.226
> -     | 192.168.1.0/24 | 192.168.1.0/24
> -     | 192.168.1.0/24 | 192.168.1.0/25
> -     | 192.168.1.0/24 | 192.168.1.255/24
> -     | 192.168.1.0/24 | 192.168.1.255/25
> -     | 10.0.0.0/8     | 10.1.2.3/8
> -     | 10.0.0.0/32    | 10.1.2.3/8
> -     | 10.1.2.3/32    | 10.1.2.3
> -     | 10.1.2.0/24    | 10.1.2.3/24
> -     | 10.1.0.0/16    | 10.1.2.3/16
> -     | 10.0.0.0/8     | 10.1.2.3/8
> -     | 10.0.0.0/8     | 11.1.2.3/8
> -     | 10.0.0.0/8     | 9.1.2.3/8
> -(14 rows)
> + ten |      cidr          |       inet
> +-----+--------------------+------------------
> +     | 192.168.1.0/24     | 192.168.1.226/24
> +     | 192.168.1.0/24     | 192.168.1.226
> +     | 192.168.1.0/24     | 192.168.1.0/24
> +     | 192.168.1.0/24     | 192.168.1.0/25
> +     | 192.168.1.0/24     | 192.168.1.255/24
> +     | 192.168.1.0/24     | 192.168.1.255/25
> +     | 10.0.0.0/8         | 10.1.2.3/8
> +     | 10.0.0.0/32        | 10.1.2.3/8
> +     | 10.1.2.3/32        | 10.1.2.3
> +     | 10.1.2.0/24        | 10.1.2.3/24
> +     | 10.1.0.0/16        | 10.1.2.3/16
> +     | 10.0.0.0/8         | 10.1.2.3/8
> +     | 10.0.0.0/8         | 11.1.2.3/8
> +     | 10.0.0.0/8         | 9.1.2.3/8
> +     | 10:23::f1/128      | 10:23::f1/64
> +     | 10:23::8000/113    | 10:23::ffff
> +     | ::ffff:1.2.3.4/128 | ::4.3.2.1/24
> +(17 rows)
>
>  -- now test some support functions
> -SELECT '' AS ten, i AS inet, host(i), text(i) FROM INET_TBL;
> - ten |       inet       |     host      |       text
> ------+------------------+---------------+------------------
> -     | 192.168.1.226/24 | 192.168.1.226 | 192.168.1.226/24
> -     | 192.168.1.226    | 192.168.1.226 | 192.168.1.226/32
> -     | 192.168.1.0/24   | 192.168.1.0   | 192.168.1.0/24
> -     | 192.168.1.0/25   | 192.168.1.0   | 192.168.1.0/25
> -     | 192.168.1.255/24 | 192.168.1.255 | 192.168.1.255/24
> -     | 192.168.1.255/25 | 192.168.1.255 | 192.168.1.255/25
> -     | 10.1.2.3/8       | 10.1.2.3      | 10.1.2.3/8
> -     | 10.1.2.3/8       | 10.1.2.3      | 10.1.2.3/8
> -     | 10.1.2.3         | 10.1.2.3      | 10.1.2.3/32
> -     | 10.1.2.3/24      | 10.1.2.3      | 10.1.2.3/24
> -     | 10.1.2.3/16      | 10.1.2.3      | 10.1.2.3/16
> -     | 10.1.2.3/8       | 10.1.2.3      | 10.1.2.3/8
> -     | 11.1.2.3/8       | 11.1.2.3      | 11.1.2.3/8
> -     | 9.1.2.3/8        | 9.1.2.3       | 9.1.2.3/8
> -(14 rows)
> +SELECT '' AS ten, i AS inet, host(i), text(i), family(i) FROM INET_TBL;
> + ten |       inet       |     host      |       text       | family
> +-----+------------------+---------------+------------------+--------
> +     | 192.168.1.226/24 | 192.168.1.226 | 192.168.1.226/24 |      4
> +     | 192.168.1.226    | 192.168.1.226 | 192.168.1.226/32 |      4
> +     | 192.168.1.0/24   | 192.168.1.0   | 192.168.1.0/24   |      4
> +     | 192.168.1.0/25   | 192.168.1.0   | 192.168.1.0/25   |      4
> +     | 192.168.1.255/24 | 192.168.1.255 | 192.168.1.255/24 |      4
> +     | 192.168.1.255/25 | 192.168.1.255 | 192.168.1.255/25 |      4
> +     | 10.1.2.3/8       | 10.1.2.3      | 10.1.2.3/8       |      4
> +     | 10.1.2.3/8       | 10.1.2.3      | 10.1.2.3/8       |      4
> +     | 10.1.2.3         | 10.1.2.3      | 10.1.2.3/32      |      4
> +     | 10.1.2.3/24      | 10.1.2.3      | 10.1.2.3/24      |      4
> +     | 10.1.2.3/16      | 10.1.2.3      | 10.1.2.3/16      |      4
> +     | 10.1.2.3/8       | 10.1.2.3      | 10.1.2.3/8       |      4
> +     | 11.1.2.3/8       | 11.1.2.3      | 11.1.2.3/8       |      4
> +     | 9.1.2.3/8        | 9.1.2.3       | 9.1.2.3/8        |      4
> +     | 10:23::f1/64     | 10:23::f1     | 10:23::f1/64     |      6
> +     | 10:23::ffff      | 10:23::ffff   | 10:23::ffff/128  |      6
> +     | ::4.3.2.1/24     | ::4.3.2.1     | ::4.3.2.1/24     |      6
> +(17 rows)
>
>  SELECT '' AS ten, c AS cidr, broadcast(c),
>    i AS inet, broadcast(i) FROM INET_TBL;
> - ten |      cidr      |    broadcast     |       inet       |    broadcast
> ------+----------------+------------------+------------------+------------------
> -     | 192.168.1.0/24 | 192.168.1.255/24 | 192.168.1.226/24 | 192.168.1.255/24
> -     | 192.168.1.0/24 | 192.168.1.255/24 | 192.168.1.226    | 192.168.1.226
> -     | 192.168.1.0/24 | 192.168.1.255/24 | 192.168.1.0/24   | 192.168.1.255/24
> -     | 192.168.1.0/24 | 192.168.1.255/24 | 192.168.1.0/25   | 192.168.1.127/25
> -     | 192.168.1.0/24 | 192.168.1.255/24 | 192.168.1.255/24 | 192.168.1.255/24
> -     | 192.168.1.0/24 | 192.168.1.255/24 | 192.168.1.255/25 | 192.168.1.255/25
> -     | 10.0.0.0/8     | 10.255.255.255/8 | 10.1.2.3/8       | 10.255.255.255/8
> -     | 10.0.0.0/32    | 10.0.0.0         | 10.1.2.3/8       | 10.255.255.255/8
> -     | 10.1.2.3/32    | 10.1.2.3         | 10.1.2.3         | 10.1.2.3
> -     | 10.1.2.0/24    | 10.1.2.255/24    | 10.1.2.3/24      | 10.1.2.255/24
> -     | 10.1.0.0/16    | 10.1.255.255/16  | 10.1.2.3/16      | 10.1.255.255/16
> -     | 10.0.0.0/8     | 10.255.255.255/8 | 10.1.2.3/8       | 10.255.255.255/8
> -     | 10.0.0.0/8     | 10.255.255.255/8 | 11.1.2.3/8       | 11.255.255.255/8
> -     | 10.0.0.0/8     | 10.255.255.255/8 | 9.1.2.3/8        | 9.255.255.255/8
> -(14 rows)
> + ten |      cidr          |    broadcast     |       inet       |               broadcast
> +-----+--------------------+------------------+------------------+---------------------------------------
> +     | 192.168.1.0/24     | 192.168.1.255/24 | 192.168.1.226/24 | 192.168.1.255/24
> +     | 192.168.1.0/24     | 192.168.1.255/24 | 192.168.1.226    | 192.168.1.226
> +     | 192.168.1.0/24     | 192.168.1.255/24 | 192.168.1.0/24   | 192.168.1.255/24
> +     | 192.168.1.0/24     | 192.168.1.255/24 | 192.168.1.0/25   | 192.168.1.127/25
> +     | 192.168.1.0/24     | 192.168.1.255/24 | 192.168.1.255/24 | 192.168.1.255/24
> +     | 192.168.1.0/24     | 192.168.1.255/24 | 192.168.1.255/25 | 192.168.1.255/25
> +     | 10.0.0.0/8         | 10.255.255.255/8 | 10.1.2.3/8       | 10.255.255.255/8
> +     | 10.0.0.0/32        | 10.0.0.0         | 10.1.2.3/8       | 10.255.255.255/8
> +     | 10.1.2.3/32        | 10.1.2.3         | 10.1.2.3         | 10.1.2.3
> +     | 10.1.2.0/24        | 10.1.2.255/24    | 10.1.2.3/24      | 10.1.2.255/24
> +     | 10.1.0.0/16        | 10.1.255.255/16  | 10.1.2.3/16      | 10.1.255.255/16
> +     | 10.0.0.0/8         | 10.255.255.255/8 | 10.1.2.3/8       | 10.255.255.255/8
> +     | 10.0.0.0/8         | 10.255.255.255/8 | 11.1.2.3/8       | 11.255.255.255/8
> +     | 10.0.0.0/8         | 10.255.255.255/8 | 9.1.2.3/8        | 9.255.255.255/8
> +     | 10:23::f1/128      | 10:23::f1        | 10:23::f1/64     | 10:23::ffff:ffff:ffff:ffff/64
> +     | 10:23::8000/113    | 10:23::ffff/113  | 10:23::ffff      | 10:23::ffff
> +     | ::ffff:1.2.3.4/128 | ::ffff:1.2.3.4   | ::4.3.2.1/24     | 0:ff:ffff:ffff:ffff:ffff:ffff:ffff/24
> +(17 rows)
>
>  SELECT '' AS ten, c AS cidr, network(c) AS "network(cidr)",
>    i AS inet, network(i) AS "network(inet)" FROM INET_TBL;
> - ten |      cidr      | network(cidr)  |       inet       |  network(inet)
> ------+----------------+----------------+------------------+------------------
> -     | 192.168.1.0/24 | 192.168.1.0/24 | 192.168.1.226/24 | 192.168.1.0/24
> -     | 192.168.1.0/24 | 192.168.1.0/24 | 192.168.1.226    | 192.168.1.226/32
> -     | 192.168.1.0/24 | 192.168.1.0/24 | 192.168.1.0/24   | 192.168.1.0/24
> -     | 192.168.1.0/24 | 192.168.1.0/24 | 192.168.1.0/25   | 192.168.1.0/25
> -     | 192.168.1.0/24 | 192.168.1.0/24 | 192.168.1.255/24 | 192.168.1.0/24
> -     | 192.168.1.0/24 | 192.168.1.0/24 | 192.168.1.255/25 | 192.168.1.128/25
> -     | 10.0.0.0/8     | 10.0.0.0/8     | 10.1.2.3/8       | 10.0.0.0/8
> -     | 10.0.0.0/32    | 10.0.0.0/32    | 10.1.2.3/8       | 10.0.0.0/8
> -     | 10.1.2.3/32    | 10.1.2.3/32    | 10.1.2.3         | 10.1.2.3/32
> -     | 10.1.2.0/24    | 10.1.2.0/24    | 10.1.2.3/24      | 10.1.2.0/24
> -     | 10.1.0.0/16    | 10.1.0.0/16    | 10.1.2.3/16      | 10.1.0.0/16
> -     | 10.0.0.0/8     | 10.0.0.0/8     | 10.1.2.3/8       | 10.0.0.0/8
> -     | 10.0.0.0/8     | 10.0.0.0/8     | 11.1.2.3/8       | 11.0.0.0/8
> -     | 10.0.0.0/8     | 10.0.0.0/8     | 9.1.2.3/8        | 9.0.0.0/8
> -(14 rows)
> + ten |        cidr        |  network(cidr)     |       inet       |  network(inet)
> +-----+--------------------+--------------------+------------------+------------------
> +     | 192.168.1.0/24     | 192.168.1.0/24     | 192.168.1.226/24 | 192.168.1.0/24
> +     | 192.168.1.0/24     | 192.168.1.0/24     | 192.168.1.226    | 192.168.1.226/32
> +     | 192.168.1.0/24     | 192.168.1.0/24     | 192.168.1.0/24   | 192.168.1.0/24
> +     | 192.168.1.0/24     | 192.168.1.0/24     | 192.168.1.0/25   | 192.168.1.0/25
> +     | 192.168.1.0/24     | 192.168.1.0/24     | 192.168.1.255/24 | 192.168.1.0/24
> +     | 192.168.1.0/24     | 192.168.1.0/24     | 192.168.1.255/25 | 192.168.1.128/25
> +     | 10.0.0.0/8         | 10.0.0.0/8         | 10.1.2.3/8       | 10.0.0.0/8
> +     | 10.0.0.0/32        | 10.0.0.0/32        | 10.1.2.3/8       | 10.0.0.0/8
> +     | 10.1.2.3/32        | 10.1.2.3/32        | 10.1.2.3         | 10.1.2.3/32
> +     | 10.1.2.0/24        | 10.1.2.0/24        | 10.1.2.3/24      | 10.1.2.0/24
> +     | 10.1.0.0/16        | 10.1.0.0/16        | 10.1.2.3/16      | 10.1.0.0/16
> +     | 10.0.0.0/8         | 10.0.0.0/8         | 10.1.2.3/8       | 10.0.0.0/8
> +     | 10.0.0.0/8         | 10.0.0.0/8         | 11.1.2.3/8       | 11.0.0.0/8
> +     | 10.0.0.0/8         | 10.0.0.0/8         | 9.1.2.3/8        | 9.0.0.0/8
> +     | 10:23::f1/128      | 10:23::f1/128      | 10:23::f1/64     | 10:23::/64
> +     | 10:23::8000/113    | 10:23::8000/113    | 10:23::ffff      | 10:23::ffff/128
> +     | ::ffff:1.2.3.4/128 | ::ffff:1.2.3.4/128 | ::4.3.2.1/24     | ::/24
> +(17 rows)
>
>  SELECT '' AS ten, c AS cidr, masklen(c) AS "masklen(cidr)",
>    i AS inet, masklen(i) AS "masklen(inet)" FROM INET_TBL;
> - ten |      cidr      | masklen(cidr) |       inet       | masklen(inet)
> ------+----------------+---------------+------------------+---------------
> -     | 192.168.1.0/24 |            24 | 192.168.1.226/24 |            24
> -     | 192.168.1.0/24 |            24 | 192.168.1.226    |            32
> -     | 192.168.1.0/24 |            24 | 192.168.1.0/24   |            24
> -     | 192.168.1.0/24 |            24 | 192.168.1.0/25   |            25
> -     | 192.168.1.0/24 |            24 | 192.168.1.255/24 |            24
> -     | 192.168.1.0/24 |            24 | 192.168.1.255/25 |            25
> -     | 10.0.0.0/8     |             8 | 10.1.2.3/8       |             8
> -     | 10.0.0.0/32    |            32 | 10.1.2.3/8       |             8
> -     | 10.1.2.3/32    |            32 | 10.1.2.3         |            32
> -     | 10.1.2.0/24    |            24 | 10.1.2.3/24      |            24
> -     | 10.1.0.0/16    |            16 | 10.1.2.3/16      |            16
> -     | 10.0.0.0/8     |             8 | 10.1.2.3/8       |             8
> -     | 10.0.0.0/8     |             8 | 11.1.2.3/8       |             8
> -     | 10.0.0.0/8     |             8 | 9.1.2.3/8        |             8
> -(14 rows)
> + ten |        cidr        | masklen(cidr) |       inet       | masklen(inet)
> +-----+--------------------+---------------+------------------+---------------
> +     | 192.168.1.0/24     |            24 | 192.168.1.226/24 |            24
> +     | 192.168.1.0/24     |            24 | 192.168.1.226    |            32
> +     | 192.168.1.0/24     |            24 | 192.168.1.0/24   |            24
> +     | 192.168.1.0/24     |            24 | 192.168.1.0/25   |            25
> +     | 192.168.1.0/24     |            24 | 192.168.1.255/24 |            24
> +     | 192.168.1.0/24     |            24 | 192.168.1.255/25 |            25
> +     | 10.0.0.0/8         |             8 | 10.1.2.3/8       |             8
> +     | 10.0.0.0/32        |            32 | 10.1.2.3/8       |             8
> +     | 10.1.2.3/32        |            32 | 10.1.2.3         |            32
> +     | 10.1.2.0/24        |            24 | 10.1.2.3/24      |            24
> +     | 10.1.0.0/16        |            16 | 10.1.2.3/16      |            16
> +     | 10.0.0.0/8         |             8 | 10.1.2.3/8       |             8
> +     | 10.0.0.0/8         |             8 | 11.1.2.3/8       |             8
> +     | 10.0.0.0/8         |             8 | 9.1.2.3/8        |             8
> +     | 10:23::f1/128      |           128 | 10:23::f1/64     |            64
> +     | 10:23::8000/113    |           113 | 10:23::ffff      |           128
> +     | ::ffff:1.2.3.4/128 |           128 | ::4.3.2.1/24     |            24
> +(17 rows)
>
>  SELECT '' AS four, c AS cidr, masklen(c) AS "masklen(cidr)",
>    i AS inet, masklen(i) AS "masklen(inet)" FROM INET_TBL
> @@ -149,23 +171,26 @@
>    i << c AS sb, i <<= c AS sbe,
>    i >> c AS sup, i >>= c AS spe
>    FROM INET_TBL;
> - ten |        i         |       c        | lt | le | eq | ge | gt | ne | sb | sbe | sup | spe
> ------+------------------+----------------+----+----+----+----+----+----+----+-----+-----+-----
> -     | 192.168.1.226/24 | 192.168.1.0/24 | f  | f  | f  | t  | t  | t  | f  | t   | f   | t
> -     | 192.168.1.226    | 192.168.1.0/24 | f  | f  | f  | t  | t  | t  | t  | t   | f   | f
> -     | 192.168.1.0/24   | 192.168.1.0/24 | f  | t  | t  | t  | f  | f  | f  | t   | f   | t
> -     | 192.168.1.0/25   | 192.168.1.0/24 | f  | f  | f  | t  | t  | t  | t  | t   | f   | f
> -     | 192.168.1.255/24 | 192.168.1.0/24 | f  | f  | f  | t  | t  | t  | f  | t   | f   | t
> -     | 192.168.1.255/25 | 192.168.1.0/24 | f  | f  | f  | t  | t  | t  | t  | t   | f   | f
> -     | 10.1.2.3/8       | 10.0.0.0/8     | f  | f  | f  | t  | t  | t  | f  | t   | f   | t
> -     | 10.1.2.3/8       | 10.0.0.0/32    | t  | t  | f  | f  | f  | t  | f  | f   | t   | t
> -     | 10.1.2.3         | 10.1.2.3/32    | f  | t  | t  | t  | f  | f  | f  | t   | f   | t
> -     | 10.1.2.3/24      | 10.1.2.0/24    | f  | f  | f  | t  | t  | t  | f  | t   | f   | t
> -     | 10.1.2.3/16      | 10.1.0.0/16    | f  | f  | f  | t  | t  | t  | f  | t   | f   | t
> -     | 10.1.2.3/8       | 10.0.0.0/8     | f  | f  | f  | t  | t  | t  | f  | t   | f   | t
> -     | 11.1.2.3/8       | 10.0.0.0/8     | f  | f  | f  | t  | t  | t  | f  | f   | f   | f
> -     | 9.1.2.3/8        | 10.0.0.0/8     | t  | t  | f  | f  | f  | t  | f  | f   | f   | f
> -(14 rows)
> + ten |        i         |       c            | lt | le | eq | ge | gt | ne | sb | sbe | sup | spe
> +-----+------------------+--------------------+----+----+----+----+----+----+----+-----+-----+-----
> +     | 192.168.1.226/24 | 192.168.1.0/24     | f  | f  | f  | t  | t  | t  | f  | t   | f   | t
> +     | 192.168.1.226    | 192.168.1.0/24     | f  | f  | f  | t  | t  | t  | t  | t   | f   | f
> +     | 192.168.1.0/24   | 192.168.1.0/24     | f  | t  | t  | t  | f  | f  | f  | t   | f   | t
> +     | 192.168.1.0/25   | 192.168.1.0/24     | f  | f  | f  | t  | t  | t  | t  | t   | f   | f
> +     | 192.168.1.255/24 | 192.168.1.0/24     | f  | f  | f  | t  | t  | t  | f  | t   | f   | t
> +     | 192.168.1.255/25 | 192.168.1.0/24     | f  | f  | f  | t  | t  | t  | t  | t   | f   | f
> +     | 10.1.2.3/8       | 10.0.0.0/8         | f  | f  | f  | t  | t  | t  | f  | t   | f   | t
> +     | 10.1.2.3/8       | 10.0.0.0/32        | t  | t  | f  | f  | f  | t  | f  | f   | t   | t
> +     | 10.1.2.3         | 10.1.2.3/32        | f  | t  | t  | t  | f  | f  | f  | t   | f   | t
> +     | 10.1.2.3/24      | 10.1.2.0/24        | f  | f  | f  | t  | t  | t  | f  | t   | f   | t
> +     | 10.1.2.3/16      | 10.1.0.0/16        | f  | f  | f  | t  | t  | t  | f  | t   | f   | t
> +     | 10.1.2.3/8       | 10.0.0.0/8         | f  | f  | f  | t  | t  | t  | f  | t   | f   | t
> +     | 11.1.2.3/8       | 10.0.0.0/8         | f  | f  | f  | t  | t  | t  | f  | f   | f   | f
> +     | 9.1.2.3/8        | 10.0.0.0/8         | t  | t  | f  | f  | f  | t  | f  | f   | f   | f
> +     | 10:23::f1/64     | 10:23::f1/128      | t  | t  | f  | f  | f  | t  | f  | f   | t   | t
> +     | 10:23::ffff      | 10:23::8000/113    | f  | f  | f  | t  | t  | t  | t  | t   | f   | f
> +     | ::4.3.2.1/24     | ::ffff:1.2.3.4/128 | t  | t  | f  | f  | f  | t  | f  | f   | t   | t
> +(17 rows)
>
>  -- check the conversion to/from text and set_netmask
>  select '' AS ten, set_masklen(inet(text(i)), 24) FROM INET_TBL;
> @@ -185,7 +210,10 @@
>       | 10.1.2.3/24
>       | 11.1.2.3/24
>       | 9.1.2.3/24
> -(14 rows)
> +     | 10:23::f1/24
> +     | 10:23::ffff/24
> +     | ::4.3.2.1/24
> +(17 rows)
>
>  -- check that index works correctly
>  create index inet_idx1 on inet_tbl(i);
> diff -ur orig-postgresql-7.3.2/src/test/regress/sql/inet.sql postgresql-7.3.2/src/test/regress/sql/inet.sql
> --- orig-postgresql-7.3.2/src/test/regress/sql/inet.sql    2001-06-16 19:05:20.000000000 -0700
> +++ postgresql-7.3.2/src/test/regress/sql/inet.sql    2003-04-22 13:25:24.000000000 -0700
> @@ -20,16 +20,20 @@
>  INSERT INTO INET_TBL (c, i) VALUES ('10', '10.1.2.3/8');
>  INSERT INTO INET_TBL (c, i) VALUES ('10', '11.1.2.3/8');
>  INSERT INTO INET_TBL (c, i) VALUES ('10', '9.1.2.3/8');
> +INSERT INTO INET_TBL (c, i) VALUES ('10:23::f1', '10:23::f1/64');
> +INSERT INTO INET_TBL (c, i) VALUES ('10:23::8000/113', '10:23::ffff');
> +INSERT INTO INET_TBL (c, i) VALUES ('::ffff:1.2.3.4', '::4.3.2.1/24');
>  -- check that CIDR rejects invalid input:
>  INSERT INTO INET_TBL (c, i) VALUES ('192.168.1.2/24', '192.168.1.226');
> +INSERT INTO INET_TBL (c, i) VALUES ('1234::1234::1234', '::1.2.3.4');
>  -- check that CIDR rejects invalid input when converting from text:
>  INSERT INTO INET_TBL (c, i) VALUES (cidr('192.168.1.2/24'), '192.168.1.226');
> -
> +INSERT INTO INET_TBL (c, i) VALUES (cidr('ffff:ffff:ffff:ffff::/24'), '::192.168.1.226');
>  SELECT '' AS ten, c AS cidr, i AS inet FROM INET_TBL;
>
>  -- now test some support functions
>
> -SELECT '' AS ten, i AS inet, host(i), text(i) FROM INET_TBL;
> +SELECT '' AS ten, i AS inet, host(i), text(i), family(i) FROM INET_TBL;
>  SELECT '' AS ten, c AS cidr, broadcast(c),
>    i AS inet, broadcast(i) FROM INET_TBL;
>  SELECT '' AS ten, c AS cidr, network(c) AS "network(cidr)",
>
> ---------------------------(end of broadcast)---------------------------
> TIP 6: Have you searched our list archives?
>
> http://archives.postgresql.org
>

--
  Bruce Momjian                        |  http://candle.pha.pa.us
  pgman@candle.pha.pa.us               |  (610) 359-1001
  +  If your life is a hard drive,     |  13 Roberts Road
  +  Christ can be your backup.        |  Newtown Square, Pennsylvania 19073

Re: ipv6 patch #3

From
Bruce Momjian
Date:
I tried to apply this patch, but unfortunately, the code has drifted
quite a bit, particularly network.c.  The patch is based on September,
2002, but the file has drifted since then.  I think the problem is that
the post-September changes aren't reflected in your patch.

Would you please look over the changes made since September, then adjust
the attached patch to take things into account and resubmit?

The attached patch does apply cleanly against CVS, but does not compile.
I actually generated it by reversing the file to September, applying
your patch, then reapplying the part I reversed.

The second attached file is the changes since September.

Thanks.

---------------------------------------------------------------------------

Michael Graff wrote:
> This includes several revisions from others.
>
> Major changes:
>
>         Add ipv6 address parsing support to 'inet' and 'cidr' data types.
>
>         Regression tests for IPv6 operations added.
>
>         Documentation updated to document IPv6 bits.
>
>         Stop treating IPv4 as an "unsigned int" and IPv6 as an array of
>         characters.  Instead, always use the array of characters so we
>         can have one function fits all.  This makes bitncmp(), addressOK(),
>         and several other functions "just work" on both address families.
>
>         add family() function which returns integer 4 or 6 for IPv4 or
>         IPv6.  (See examples below)  Note that to add this new function
>         you will need to dump/initdb/reload or find the correct magic
>         to add the function to the postgresql function catalogs.
>
>         IPv4 addresses always sort before IPv6.
>
>         On disk we use AF_INET for IPv4, and AF_INET+1 for IPv6 addresses.
>         This prevents the need for a dump and reload, but lets IPv6 parsing
>         work on machines without AF_INET6.
>
>         To select all IPv4 addresses from a table:
>
>                 select * from foo where family(addr) = 4 ...
>
>         Order by and other bits should all work.
>
> Regression tests pass, at least on my NetBSD machine.
>
> --Michael

--
  Bruce Momjian                        |  http://candle.pha.pa.us
  pgman@candle.pha.pa.us               |  (610) 359-1001
  +  If your life is a hard drive,     |  13 Roberts Road
  +  Christ can be your backup.        |  Newtown Square, Pennsylvania 19073
Index: doc/src/sgml/datatype.sgml
===================================================================
RCS file: /cvsroot/pgsql-server/doc/src/sgml/datatype.sgml,v
retrieving revision 1.116
diff -c -c -r1.116 datatype.sgml
*** doc/src/sgml/datatype.sgml    13 Mar 2003 01:30:27 -0000    1.116
--- doc/src/sgml/datatype.sgml    12 Jun 2003 05:13:12 -0000
***************
*** 100,106 ****
        <row>
         <entry><type>cidr</type></entry>
         <entry></entry>
!        <entry>IP network address</entry>
        </row>

        <row>
--- 100,106 ----
        <row>
         <entry><type>cidr</type></entry>
         <entry></entry>
!        <entry>IPv4 or IPc6 network address</entry>
        </row>

        <row>
***************
*** 124,130 ****
        <row>
         <entry><type>inet</type></entry>
         <entry></entry>
!        <entry>IP host address</entry>
        </row>

        <row>
--- 124,130 ----
        <row>
         <entry><type>inet</type></entry>
         <entry></entry>
!        <entry>IPv4 or IPv6 host address</entry>
        </row>

        <row>
***************
*** 2394,2400 ****
     </indexterm>

     <para>
!     <productname>PostgreSQL</> offers data types to store IP and MAC
      addresses, shown in <xref linkend="datatype-net-types-table">.  It
      is preferable to use these types over plain text types, because
      these types offer input error checking and several specialized
--- 2394,2400 ----
     </indexterm>

     <para>
!     <productname>PostgreSQL</> offers data types to store IPv4, IPv6, and MAC
      addresses, shown in <xref linkend="datatype-net-types-table">.  It
      is preferable to use these types over plain text types, because
      these types offer input error checking and several specialized
***************
*** 2415,2428 ****

         <row>
      <entry><type>cidr</type></entry>
!     <entry>12 bytes</entry>
!     <entry>IPv4 networks</entry>
         </row>

         <row>
      <entry><type>inet</type></entry>
!     <entry>12 bytes</entry>
!     <entry>IPv4 hosts and networks</entry>
         </row>

         <row>
--- 2415,2428 ----

         <row>
      <entry><type>cidr</type></entry>
!     <entry>12 or 24 bytes</entry>
!     <entry>valid IPv4 or IPv6 networks</entry>
         </row>

         <row>
      <entry><type>inet</type></entry>
!     <entry>12 or 24 bytes</entry>
!     <entry>valid IPv4 or IPv6 hosts or networks</entry>
         </row>

         <row>
***************
*** 2436,2442 ****
      </table>

     <para>
!     IPv6 is not yet supported.
     </para>


--- 2436,2445 ----
      </table>

     <para>
!     When sorting <type>inet</type> or <type>cidr</type> data types,
!     IPv4 addresses will always sort before IPv6 addresses, including
!     IPv4 addresses encapsulated or mapped into IPv6 addresses, such as
!     ::10.2.3.4 or ::ffff::10.4.3.2.
     </para>


***************
*** 2448,2473 ****
      </indexterm>

      <para>
!      The <type>inet</type> type holds an IP host address, and
       optionally the identity of the subnet it is in, all in one field.
!      The subnet identity is represented by stating how many bits of
!      the host address represent the network address (the
!      <quote>netmask</quote>).  If the netmask is 32, then the value
!      does not indicate a subnet, only a single host.  Note that if you
!      want to accept networks only, you should use the
       <type>cidr</type> type rather than <type>inet</type>.
      </para>

      <para>
!      The input format for this type is <replaceable
!      class="parameter">x.x.x.x/y</replaceable> where <replaceable
!      class="parameter">x.x.x.x</replaceable> is an IP address and
!      <replaceable class="parameter">y</replaceable> is the number of
!      bits in the netmask.  If the <replaceable
!      class="parameter">/y</replaceable> part is left off, then the
!      netmask is 32, and the value represents just a single host.
!      On display, the <replaceable class="parameter">/y</replaceable>
!      portion is suppressed if the netmask is 32.
      </para>
     </sect2>

--- 2451,2481 ----
      </indexterm>

      <para>
!      The <type>inet</type> type holds an IPv4 or IPv6 host address, and
       optionally the identity of the subnet it is in, all in one field.
!      The subnet identity is represented by stating how many bits of the
!      the host address represent the network address
!      (the <quote>netmask</quote>).  If the netmask is 32 and the address is
!      IPv4, then the value does not indicate a subnet, only a single host.
!      In IPv6, the address length is 128 bits.
!      Note that if you want to accept networks only, you should use the
       <type>cidr</type> type rather than <type>inet</type>.
      </para>

      <para>
!      The input format for this type is
!      <replaceable class="parameter">address/y</replaceable>
!      where
!      <replaceable class="parameter">address</replaceable>
!      is an IPv4 or IPv6 address and
!      <replaceable class="parameter">y</replaceable>
!      is the number of bits in the netmask.  If the
!      <replaceable class="parameter">/y</replaceable>
!      part is left off, then the
!      netmask is 32 for IPv4 and 128 for IPv6, and the value represents
!      just a single host.  On display, the
!      <replaceable class="parameter">/y</replaceable>
!      portion is suppressed if the netmask specifies a single host.
      </para>
     </sect2>

***************
*** 2479,2490 ****
      </indexterm>

      <para>
!      The <type>cidr</type> type holds an IP network specification.
       Input and output formats follow Classless Internet Domain Routing
       conventions.
       The format for specifying networks is <replaceable
!      class="parameter">x.x.x.x/y</> where <replaceable
!      class="parameter">x.x.x.x</> is the network and <replaceable
       class="parameter">y</> is the number of bits in the netmask.  If
       <replaceable class="parameter">y</> is omitted, it is calculated
       using assumptions from the older classful network numbering system, except
--- 2487,2499 ----
      </indexterm>

      <para>
!      The <type>cidr</type> type holds an IPv4 or IPv6 network specification.
       Input and output formats follow Classless Internet Domain Routing
       conventions.
       The format for specifying networks is <replaceable
!      class="parameter">address/y</> where <replaceable
!      class="parameter">address</> is the network represented as an
!      IPv4 or IPv6 address, and <replaceable
       class="parameter">y</> is the number of bits in the netmask.  If
       <replaceable class="parameter">y</> is omitted, it is calculated
       using assumptions from the older classful network numbering system, except
***************
*** 2563,2568 ****
--- 2572,2599 ----
       <entry>10.0.0.0/8</entry>
       <entry>10/8</entry>
      </row>
+     <row>
+      <entry>10.1.2.3/32</entry>
+      <entry>10.1.2.3/32</entry>
+          <entry>10.1.2.3/32</entry>
+         <row>
+      <entry>2001:4f8:3:ba::/64</entry>
+      <entry>2001:4f8:3:ba::/64</entry>
+      <entry>2001:4f8:3:ba::/64</entry>
+     </row>
+         <row>
+      <entry>2001:4f8:3:ba:2e0:81ff:fe22:d1f1/128</entry>
+      <entry>2001:4f8:3:ba:2e0:81ff:fe22:d1f1/128</entry>
+      <entry>2001:4f8:3:ba:2e0:81ff:fe22:d1f1</entry>
+     </row>
+     <row>
+      <entry>::ffff:1.2.3.0/120</entry>
+      <entry>::ffff:1.2.3.0/120</entry>
+          <entry>::ffff:1.2.3/120</entry>
+     <row>
+      <entry>::ffff:1.2.3.0/128</entry>
+      <entry>::ffff:1.2.3.0/128</entry>
+          <entry>::ffff:1.2.3.0/128</entry>
         </tbody>
        </tgroup>
       </table>
Index: doc/src/sgml/func.sgml
===================================================================
RCS file: /cvsroot/pgsql-server/doc/src/sgml/func.sgml,v
retrieving revision 1.154
diff -c -c -r1.154 func.sgml
*** doc/src/sgml/func.sgml    5 May 2003 15:08:49 -0000    1.154
--- doc/src/sgml/func.sgml    12 Jun 2003 05:13:18 -0000
***************
*** 5804,5809 ****
--- 5804,5814 ----
      <entry><literal>inet '192.168.1.5' < inet '192.168.1.6'</literal></entry>
         </row>
         <row>
+     <entry><</entry>
+     <entry>Less than</entry>
+     <entry><literal>inet '1111::2222' < inet '2222::1111'</literal></entry>
+        </row>
+        <row>
      <entry> <literal><=</literal> </entry>
      <entry>is less than or equal</entry>
      <entry><literal>inet '192.168.1.5' <= inet '192.168.1.5'</literal></entry>
Index: src/backend/utils/adt/inet_net_ntop.c
===================================================================
RCS file: /cvsroot/pgsql-server/src/backend/utils/adt/inet_net_ntop.c,v
retrieving revision 1.12
diff -c -c -r1.12 inet_net_ntop.c
*** src/backend/utils/adt/inet_net_ntop.c    2 Sep 2002 02:47:04 -0000    1.12
--- src/backend/utils/adt/inet_net_ntop.c    12 Jun 2003 05:13:19 -0000
***************
*** 27,34 ****
--- 27,38 ----

  #include <errno.h>

+ #include "utils/inet.h"
  #include "utils/builtins.h"

+ #define NS_IN6ADDRSZ 16
+ #define NS_INT16SZ 2
+
  #ifdef SPRINTF_CHAR
  #define SPRINTF(x) strlen(sprintf/**/x)
  #else
***************
*** 36,44 ****
  #endif

  static char *inet_net_ntop_ipv4(const u_char *src, int bits,
!                    char *dst, size_t size);
  static char *inet_cidr_ntop_ipv4(const u_char *src, int bits,
!                     char *dst, size_t size);

  /*
   * char *
--- 40,52 ----
  #endif

  static char *inet_net_ntop_ipv4(const u_char *src, int bits,
!                 char *dst, size_t size);
  static char *inet_cidr_ntop_ipv4(const u_char *src, int bits,
!                  char *dst, size_t size);
! static char *inet_net_ntop_ipv6(const u_char *src, int bits,
!                 char *dst, size_t size);
! static char *inet_cidr_ntop_ipv6(const u_char *src, int bits,
!                  char *dst, size_t size);

  /*
   * char *
***************
*** 55,62 ****
  {
      switch (af)
      {
!         case AF_INET:
              return (inet_cidr_ntop_ipv4(src, bits, dst, size));
          default:
              errno = EAFNOSUPPORT;
              return (NULL);
--- 63,72 ----
  {
      switch (af)
      {
!         case PGSQL_AF_INET:
              return (inet_cidr_ntop_ipv4(src, bits, dst, size));
+         case PGSQL_AF_INET6:
+             return (inet_cidr_ntop_ipv6(src, bits, dst, size));
          default:
              errno = EAFNOSUPPORT;
              return (NULL);
***************
*** 136,141 ****
--- 146,293 ----
      return (NULL);
  }

+ /*
+  * static char *
+  * inet_net_ntop_ipv6(src, bits, fakebits, dst, size)
+  *    convert IPv6 network number from network to presentation format.
+  *    generates CIDR style result always. Picks the shortest representation
+  *    unless the IP is really IPv4.
+  *    always prints specified number of bits (bits).
+  * return:
+  *    pointer to dst, or NULL if an error occurred (check errno).
+  * note:
+  *    network byte order assumed.  this means 192.5.5.240/28 has
+  *    0x11110000 in its fourth octet.
+  * author:
+  *    Vadim Kogan (UCB), June 2001
+  *  Original version (IPv4) by Paul Vixie (ISC), July 1996
+  */
+
+ static char *
+ inet_cidr_ntop_ipv6(const u_char *src, int bits, char *dst, size_t size)
+ {
+     u_int    m;
+     int    b;
+     int    p;
+     int    zero_s, zero_l, tmp_zero_s, tmp_zero_l;
+     int    i;
+     int    is_ipv4 = 0;
+     int     double_colon = 0;
+     unsigned char inbuf[16];
+     char outbuf[sizeof("xxxx:xxxx:xxxx:xxxx:xxxx:xxxx:255.255.255.255/128")];
+     char    *cp;
+     int    words;
+     u_char    *s;
+
+     if (bits < 0 || bits > 128) {
+         errno = EINVAL;
+         return (NULL);
+     }
+
+     cp = outbuf;
+     double_colon = 0;
+
+     if (bits == 0) {
+         *cp++ = ':';
+         *cp++ = ':';
+         *cp = '\0';
+         double_colon = 1;
+     } else {
+         /* Copy src to private buffer.  Zero host part. */
+         p = (bits + 7) / 8;
+         memcpy(inbuf, src, p);
+         memset(inbuf + p, 0, 16 - p);
+         b = bits % 8;
+         if (b != 0) {
+             m = ~0 << (8 - b);
+             inbuf[p-1] &= m;
+         }
+
+         s = inbuf;
+
+         /* how many words need to be displayed in output */
+         words = (bits + 15) / 16;
+         if (words == 1)
+             words = 2;
+
+         /* Find the longest substring of zero's */
+         zero_s = zero_l = tmp_zero_s = tmp_zero_l = 0;
+         for (i = 0; i < (words * 2); i += 2) {
+             if ((s[i] | s[i+1]) == 0) {
+                 if (tmp_zero_l == 0)
+                     tmp_zero_s = i / 2;
+                 tmp_zero_l++;
+             } else {
+                 if (tmp_zero_l && zero_l < tmp_zero_l) {
+                     zero_s = tmp_zero_s;
+                     zero_l = tmp_zero_l;
+                     tmp_zero_l = 0;
+                 }
+             }
+         }
+
+         if (tmp_zero_l && zero_l < tmp_zero_l) {
+             zero_s = tmp_zero_s;
+             zero_l = tmp_zero_l;
+         }
+
+         if (zero_l != words && zero_s == 0 && ((zero_l == 6) ||
+             ((zero_l == 5 && s[10] == 0xff && s[11] == 0xff) ||
+             ((zero_l == 7 && s[14] != 0 && s[15] != 1)))))
+             is_ipv4 = 1;
+
+         /* Format whole words. */
+         for (p = 0; p < words; p++) {
+             if (zero_l != 0 && p >= zero_s && p < zero_s + zero_l) {
+                 /* Time to skip some zeros */
+                 if (p == zero_s)
+                     *cp++ = ':';
+                 if (p == words - 1) {
+                     *cp++ = ':';
+                     double_colon = 1;
+                 }
+                 s++;
+                 s++;
+                 continue;
+             }
+
+             if (is_ipv4 && p > 5 ) {
+                 *cp++ = (p == 6) ? ':' : '.';
+                 cp += SPRINTF((cp, "%u", *s++));
+                 /* we can potentially drop the last octet */
+                 if (p != 7 || bits > 120) {
+                     *cp++ = '.';
+                     cp += SPRINTF((cp, "%u", *s++));
+                 }
+             } else {
+                 if (cp != outbuf)
+                     *cp++ = ':';
+                 cp += SPRINTF((cp, "%x", *s * 256 + s[1]));
+                 s += 2;
+             }
+         }
+     }
+
+     if (!double_colon) {
+         if (bits < 128 - 32)
+             cp += SPRINTF((cp, "::", bits));
+         else if (bits < 128 - 16)
+             cp += SPRINTF((cp, ":0", bits));
+     }
+
+     /* Format CIDR /width. */
+     SPRINTF((cp, "/%u", bits));
+
+     if (strlen(outbuf) + 1 > size)
+         goto emsgsize;
+     strcpy(dst, outbuf);
+
+     return (dst);
+
+ emsgsize:
+     errno = EMSGSIZE;
+     return (NULL);
+ }

  /*
   * char *
***************
*** 156,163 ****
  {
      switch (af)
      {
!         case AF_INET:
              return (inet_net_ntop_ipv4(src, bits, dst, size));
          default:
              errno = EAFNOSUPPORT;
              return (NULL);
--- 308,317 ----
  {
      switch (af)
      {
!         case PGSQL_AF_INET:
              return (inet_net_ntop_ipv4(src, bits, dst, size));
+         case PGSQL_AF_INET6:
+             return (inet_net_ntop_ipv6(src, bits, dst, size));
          default:
              errno = EAFNOSUPPORT;
              return (NULL);
***************
*** 216,219 ****
--- 370,497 ----
  emsgsize:
      errno = EMSGSIZE;
      return (NULL);
+ }
+
+ static int
+ decoct(const u_char *src, int bytes, char *dst, size_t size) {
+     char *odst = dst;
+     char *t;
+     int b;
+
+     for (b = 1; b <= bytes; b++) {
+         if (size < sizeof "255.")
+             return (0);
+         t = dst;
+         dst += SPRINTF((dst, "%u", *src++));
+         if (b != bytes) {
+             *dst++ = '.';
+             *dst = '\0';
+         }
+         size -= (size_t)(dst - t);
+     }
+     return (dst - odst);
+ }
+
+ static char *
+ inet_net_ntop_ipv6(const u_char *src, int bits, char *dst, size_t size)
+ {
+     /*
+      * Note that int32_t and int16_t need only be "at least" large enough
+      * to contain a value of the specified size.  On some systems, like
+      * Crays, there is no such thing as an integer variable with 16 bits.
+      * Keep this in mind if you think this function should have been coded
+      * to use pointer overlays.  All the world's not a VAX.
+      */
+     char tmp[sizeof "ffff:ffff:ffff:ffff:ffff:ffff:255.255.255.255/128"];
+     char *tp;
+     struct { int base, len; } best, cur;
+     u_int words[NS_IN6ADDRSZ / NS_INT16SZ];
+     int i;
+
+     if ((bits < -1) || (bits > 128)) {
+         errno = EINVAL;
+         return (NULL);
+     }
+
+     /*
+      * Preprocess:
+      *    Copy the input (bytewise) array into a wordwise array.
+      *    Find the longest run of 0x00's in src[] for :: shorthanding.
+      */
+     memset(words, '\0', sizeof words);
+     for (i = 0; i < NS_IN6ADDRSZ; i++)
+         words[i / 2] |= (src[i] << ((1 - (i % 2)) << 3));
+     best.base = -1;
+     cur.base = -1;
+     for (i = 0; i < (NS_IN6ADDRSZ / NS_INT16SZ); i++) {
+         if (words[i] == 0) {
+             if (cur.base == -1)
+                 cur.base = i, cur.len = 1;
+             else
+                 cur.len++;
+         } else {
+             if (cur.base != -1) {
+                 if (best.base == -1 || cur.len > best.len)
+                     best = cur;
+                 cur.base = -1;
+             }
+         }
+     }
+     if (cur.base != -1) {
+         if (best.base == -1 || cur.len > best.len)
+             best = cur;
+     }
+     if (best.base != -1 && best.len < 2)
+         best.base = -1;
+
+     /*
+      * Format the result.
+      */
+     tp = tmp;
+     for (i = 0; i < (NS_IN6ADDRSZ / NS_INT16SZ); i++) {
+         /* Are we inside the best run of 0x00's? */
+         if (best.base != -1 && i >= best.base &&
+             i < (best.base + best.len)) {
+             if (i == best.base)
+                 *tp++ = ':';
+             continue;
+         }
+         /* Are we following an initial run of 0x00s or any real hex? */
+         if (i != 0)
+             *tp++ = ':';
+         /* Is this address an encapsulated IPv4? */
+         if (i == 6 && best.base == 0 && (best.len == 6 ||
+             (best.len == 7 && words[7] != 0x0001) ||
+             (best.len == 5 && words[5] == 0xffff))) {
+             int n;
+
+             n = decoct(src+12, 4, tp, sizeof tmp - (tp - tmp));
+             if (n == 0) {
+                 errno = EMSGSIZE;
+                 return (NULL);
+             }
+             tp += strlen(tp);
+             break;
+         }
+         tp += SPRINTF((tp, "%x", words[i]));
+     }
+
+     /* Was it a trailing run of 0x00's? */
+     if (best.base != -1 && (best.base + best.len) ==
+         (NS_IN6ADDRSZ / NS_INT16SZ))
+         *tp++ = ':';
+     *tp = '\0';
+
+     if (bits != -1 && bits != 128)
+         tp += SPRINTF((tp, "/%u", bits));
+
+     /*
+      * Check for overflow, copy, and we're done.
+      */
+     if ((size_t)(tp - tmp) > size) {
+         errno = EMSGSIZE;
+         return (NULL);
+     }
+     strcpy(dst, tmp);
+     return (dst);
  }
Index: src/backend/utils/adt/inet_net_pton.c
===================================================================
RCS file: /cvsroot/pgsql-server/src/backend/utils/adt/inet_net_pton.c,v
retrieving revision 1.14
diff -c -c -r1.14 inet_net_pton.c
*** src/backend/utils/adt/inet_net_pton.c    2 Sep 2002 02:47:04 -0000    1.14
--- src/backend/utils/adt/inet_net_pton.c    12 Jun 2003 05:13:19 -0000
***************
*** 29,44 ****
  #include <ctype.h>
  #include <errno.h>

! #include "utils/builtins.h"

! #ifdef SPRINTF_CHAR
! #define SPRINTF(x) strlen(sprintf/**/x)
! #else
! #define SPRINTF(x) ((size_t)sprintf x)
! #endif

  static int    inet_net_pton_ipv4(const char *src, u_char *dst);
  static int    inet_cidr_pton_ipv4(const char *src, u_char *dst, size_t size);

  /*
   * static int
--- 29,42 ----
  #include <ctype.h>
  #include <errno.h>

! #include "utils/inet.h"

! #include "utils/builtins.h"

  static int    inet_net_pton_ipv4(const char *src, u_char *dst);
  static int    inet_cidr_pton_ipv4(const char *src, u_char *dst, size_t size);
+ static int      inet_net_pton_ipv6(const char *src, u_char *dst);
+ static int      inet_cidr_pton_ipv6(const char *src, u_char *dst, size_t size);

  /*
   * static int
***************
*** 63,72 ****
  {
      switch (af)
      {
!         case AF_INET:
              return size == -1 ?
                  inet_net_pton_ipv4(src, dst) :
                  inet_cidr_pton_ipv4(src, dst, size);
          default:
              errno = EAFNOSUPPORT;
              return (-1);
--- 61,74 ----
  {
      switch (af)
      {
!         case PGSQL_AF_INET:
              return size == -1 ?
                  inet_net_pton_ipv4(src, dst) :
                  inet_cidr_pton_ipv4(src, dst, size);
+         case PGSQL_AF_INET6:
+             return size == -1 ?
+                 inet_net_pton_ipv6(src, dst) :
+                 inet_cidr_pton_ipv6(src, dst, size);
          default:
              errno = EAFNOSUPPORT;
              return (-1);
***************
*** 332,337 ****
--- 334,535 ----
      return (-1);

  emsgsize:
+     errno = EMSGSIZE;
+     return (-1);
+ }
+
+ static int
+ getbits(const char *src, int *bitsp) {
+     static const char digits[] = "0123456789";
+     int n;
+     int val;
+     char ch;
+
+     val = 0;
+     n = 0;
+     while ((ch = *src++) != '\0') {
+         const char *pch;
+
+         pch = strchr(digits, ch);
+         if (pch != NULL) {
+             if (n++ != 0 && val == 0)    /* no leading zeros */
+                 return (0);
+             val *= 10;
+             val += (pch - digits);
+             if (val > 128)            /* range */
+                 return (0);
+             continue;
+         }
+         return (0);
+     }
+     if (n == 0)
+         return (0);
+     *bitsp = val;
+     return (1);
+ }
+
+ static int
+ getv4(const char *src, u_char *dst, int *bitsp) {
+     static const char digits[] = "0123456789";
+     u_char *odst = dst;
+     int n;
+     u_int val;
+     char ch;
+
+     val = 0;
+     n = 0;
+     while ((ch = *src++) != '\0') {
+         const char *pch;
+
+         pch = strchr(digits, ch);
+         if (pch != NULL) {
+             if (n++ != 0 && val == 0)    /* no leading zeros */
+                 return (0);
+             val *= 10;
+             val += (pch - digits);
+             if (val > 255)            /* range */
+                 return (0);
+             continue;
+         }
+         if (ch == '.' || ch == '/') {
+             if (dst - odst > 3)        /* too many octets? */
+                 return (0);
+             *dst++ = val;
+             if (ch == '/')
+                 return (getbits(src, bitsp));
+             val = 0;
+             n = 0;
+             continue;
+         }
+         return (0);
+     }
+     if (n == 0)
+         return (0);
+     if (dst - odst > 3)        /* too many octets? */
+         return (0);
+     *dst++ = val;
+     return (1);
+ }
+
+ static int
+ inet_net_pton_ipv6(const char *src, u_char *dst)
+ {
+     return inet_cidr_pton_ipv6(src, dst, 16);
+ }
+
+ #define NS_IN6ADDRSZ 16
+ #define NS_INT16SZ 2
+ #define NS_INADDRSZ 4
+
+ static int
+ inet_cidr_pton_ipv6(const char *src, u_char *dst, size_t size) {
+     static const char xdigits_l[] = "0123456789abcdef",
+               xdigits_u[] = "0123456789ABCDEF";
+     u_char tmp[NS_IN6ADDRSZ], *tp, *endp, *colonp;
+     const char *xdigits, *curtok;
+     int ch, saw_xdigit;
+     u_int val;
+     int digits;
+     int bits;
+
+     if (size < NS_IN6ADDRSZ)
+         goto emsgsize;
+
+     memset((tp = tmp), '\0', NS_IN6ADDRSZ);
+     endp = tp + NS_IN6ADDRSZ;
+     colonp = NULL;
+     /* Leading :: requires some special handling. */
+     if (*src == ':')
+         if (*++src != ':')
+             goto enoent;
+     curtok = src;
+     saw_xdigit = 0;
+     val = 0;
+     digits = 0;
+     bits = -1;
+     while ((ch = *src++) != '\0') {
+         const char *pch;
+
+         if ((pch = strchr((xdigits = xdigits_l), ch)) == NULL)
+             pch = strchr((xdigits = xdigits_u), ch);
+         if (pch != NULL) {
+             val <<= 4;
+             val |= (pch - xdigits);
+             if (++digits > 4)
+                 goto enoent;
+             saw_xdigit = 1;
+             continue;
+         }
+         if (ch == ':') {
+             curtok = src;
+             if (!saw_xdigit) {
+                 if (colonp)
+                     goto enoent;
+                 colonp = tp;
+                 continue;
+             } else if (*src == '\0')
+                 goto enoent;
+             if (tp + NS_INT16SZ > endp)
+                 return (0);
+             *tp++ = (u_char) (val >> 8) & 0xff;
+             *tp++ = (u_char) val & 0xff;
+             saw_xdigit = 0;
+             digits = 0;
+             val = 0;
+             continue;
+         }
+         if (ch == '.' && ((tp + NS_INADDRSZ) <= endp) &&
+              getv4(curtok, tp, &bits) > 0) {
+             tp += NS_INADDRSZ;
+             saw_xdigit = 0;
+             break;    /* '\0' was seen by inet_pton4(). */
+         }
+         if (ch == '/' && getbits(src, &bits) > 0)
+             break;
+         goto enoent;
+     }
+     if (saw_xdigit) {
+         if (tp + NS_INT16SZ > endp)
+             goto enoent;
+         *tp++ = (u_char) (val >> 8) & 0xff;
+         *tp++ = (u_char) val & 0xff;
+     }
+     if (bits == -1)
+         bits = 128;
+
+     endp =  tmp + 16;
+
+     if (colonp != NULL) {
+         /*
+          * Since some memmove()'s erroneously fail to handle
+          * overlapping regions, we'll do the shift by hand.
+          */
+         const int n = tp - colonp;
+         int i;
+
+         if (tp == endp)
+             goto enoent;
+         for (i = 1; i <= n; i++) {
+             endp[- i] = colonp[n - i];
+             colonp[n - i] = 0;
+         }
+         tp = endp;
+     }
+     if (tp != endp)
+         goto enoent;
+
+     /*
+      * Copy out the result.
+      */
+     memcpy(dst, tmp, NS_IN6ADDRSZ);
+
+     return (bits);
+
+  enoent:
+     errno = ENOENT;
+     return (-1);
+
+  emsgsize:
      errno = EMSGSIZE;
      return (-1);
  }
Index: src/backend/utils/adt/network.c
===================================================================
RCS file: /cvsroot/pgsql-server/src/backend/utils/adt/network.c,v
retrieving revision 1.41
diff -c -c -r1.41 network.c
*** src/backend/utils/adt/network.c    13 May 2003 18:03:07 -0000    1.41
--- src/backend/utils/adt/network.c    12 Jun 2003 05:13:20 -0000
***************
*** 1,7 ****
  /*
!  *    PostgreSQL type definitions for the INET type.    This
!  *    is for IP V4 CIDR notation, but prepared for V6: just
!  *    add the necessary bits where the comments indicate.
   *
   *    $Header: /cvsroot/pgsql-server/src/backend/utils/adt/network.c,v 1.41 2003/05/13 18:03:07 tgl Exp $
   *
--- 1,5 ----
  /*
!  *    PostgreSQL type definitions for the INET and CIDR types.
   *
   *    $Header: /cvsroot/pgsql-server/src/backend/utils/adt/network.c,v 1.41 2003/05/13 18:03:07 tgl Exp $
   *
***************
*** 15,38 ****
  #include <netinet/in.h>
  #include <arpa/inet.h>

  #include "catalog/pg_type.h"
  #include "libpq/pqformat.h"
  #include "utils/builtins.h"
  #include "utils/inet.h"

-
  static Datum text_network(text *src, int type);
  static int32 network_cmp_internal(inet *a1, inet *a2);
! static int    v4bitncmp(unsigned long a1, unsigned long a2, int bits);
! static bool v4addressOK(unsigned long a1, int bits);

  /*
!  *    Access macros.    Add IPV6 support.
   */

- #define ip_addrsize(inetptr) \
-     (((inet_struct *)VARDATA(inetptr))->family == AF_INET ? 4 : -1)
-
  #define ip_family(inetptr) \
      (((inet_struct *)VARDATA(inetptr))->family)

--- 13,35 ----
  #include <netinet/in.h>
  #include <arpa/inet.h>

+ #include <assert.h>
+
  #include "catalog/pg_type.h"
  #include "libpq/pqformat.h"
  #include "utils/builtins.h"
  #include "utils/inet.h"

  static Datum text_network(text *src, int type);
  static int32 network_cmp_internal(inet *a1, inet *a2);
! static int bitncmp(void *l, void *r, int n);
! static bool addressOK(unsigned char *a, int bits, int family);
! static int ip_addrsize(inet *inetptr);

  /*
!  *    Access macros.
   */

  #define ip_family(inetptr) \
      (((inet_struct *)VARDATA(inetptr))->family)

***************
*** 42,83 ****
  #define ip_type(inetptr) \
      (((inet_struct *)VARDATA(inetptr))->type)

! #define ip_v4addr(inetptr) \
!     (((inet_struct *)VARDATA(inetptr))->addr.ipv4_addr)

  /* Common input routine */
  static inet *
  network_in(char *src, int type)
  {
!     int            bits;
      inet       *dst;

      /* make sure any unused bits in a CIDR value are zeroed */
      dst = (inet *) palloc0(VARHDRSZ + sizeof(inet_struct));

!     /* First, try for an IP V4 address: */
!     ip_family(dst) = AF_INET;
!     bits = inet_net_pton(ip_family(dst), src, &ip_v4addr(dst),
!                          type ? ip_addrsize(dst) : -1);
!     if ((bits < 0) || (bits > 32))
!     {
!         /* Go for an IPV6 address here, before faulting out: */
!         elog(ERROR, "invalid %s value '%s'",
!              type ? "CIDR" : "INET", src);
      }

      /*
!      * Error check: CIDR values must not have any bits set beyond the
!      * masklen. XXX this code is not IPV6 ready.
       */
      if (type)
      {
!         if (!v4addressOK(ip_v4addr(dst), bits))
              elog(ERROR, "invalid CIDR value '%s': has bits set to right of mask", src);
      }

      VARATT_SIZEP(dst) = VARHDRSZ
!         + ((char *) &ip_v4addr(dst) - (char *) VARDATA(dst))
          + ip_addrsize(dst);
      ip_bits(dst) = bits;
      ip_type(dst) = type;
--- 39,107 ----
  #define ip_type(inetptr) \
      (((inet_struct *)VARDATA(inetptr))->type)

! #define ip_addr(inetptr) \
!     (((inet_struct *)VARDATA(inetptr))->ip_addr)
!
! #define ip_maxbits(inetptr) \
!     (ip_family(inetptr) == PGSQL_AF_INET ? 32 : 128)
!
! /*
!  * Now, as a function!
!  * Return the number of bytes of storage needed for this data type.
!  */
! static int
! ip_addrsize(inet *inetptr)
! {
!     switch (ip_family(inetptr)) {
!     case PGSQL_AF_INET:
!         return 4;
!     case PGSQL_AF_INET6:
!         return 16;
!     default:
!         return -1;
!     }
! }

  /* Common input routine */
  static inet *
  network_in(char *src, int type)
  {
!     int        bits;
      inet       *dst;

      /* make sure any unused bits in a CIDR value are zeroed */
      dst = (inet *) palloc0(VARHDRSZ + sizeof(inet_struct));

!     /*
!      * First, check to see if this is an IPv6 or IPv4 address.  IPv6
!      * addresses will have a : somewhere in them (several, in fact) so
!      * if there is one present, assume it's V6, otherwise assume it's V4.
!      */
!
!     if (strchr(src, ':') != NULL) {
!         ip_family(dst) = PGSQL_AF_INET6;
!     } else {
!         ip_family(dst) = PGSQL_AF_INET;
      }

+     bits = inet_net_pton(ip_family(dst), src, ip_addr(dst),
+                  type ? ip_addrsize(dst) : -1);
+     if ((bits < 0) || (bits > ip_maxbits(dst)))
+         elog(ERROR, "invalid %s value '%s'",
+              type ? "CIDR" : "INET", src);
+
      /*
!      * Error check: CIDR values must not have any bits set beyond
!      * the masklen.
       */
      if (type)
      {
!         if (!addressOK(ip_addr(dst), bits, ip_family(dst)))
              elog(ERROR, "invalid CIDR value '%s': has bits set to right of mask", src);
      }

      VARATT_SIZEP(dst) = VARHDRSZ
!         + ((char *) &ip_addr(dst) - (char *) VARDATA(dst))
          + ip_addrsize(dst);
      ip_bits(dst) = bits;
      ip_type(dst) = type;
***************
*** 110,141 ****
  inet_out(PG_FUNCTION_ARGS)
  {
      inet       *src = PG_GETARG_INET_P(0);
!     char        tmp[sizeof("255.255.255.255/32")];
      char       *dst;
      int            len;

!     if (ip_family(src) == AF_INET)
      {
!         /* It's an IP V4 address: */
!
!         /*
!          * Use inet style for both inet and cidr, since we don't want
!          * abbreviated CIDR style here.
!          */
!         dst = inet_net_ntop(AF_INET, &ip_v4addr(src), ip_bits(src),
!                             tmp, sizeof(tmp));
!         if (dst == NULL)
!             elog(ERROR, "unable to print address (%s)", strerror(errno));
!         /* For CIDR, add /n if not present */
!         if (ip_type(src) && strchr(tmp, '/') == NULL)
!         {
!             len = strlen(tmp);
!             snprintf(tmp + len, sizeof(tmp) - len, "/%u", ip_bits(src));
!         }
      }
-     else
-         /* Go for an IPV6 address here, before faulting out: */
-         elog(ERROR, "unknown address family (%d)", ip_family(src));

      PG_RETURN_CSTRING(pstrdup(tmp));
  }
--- 134,153 ----
  inet_out(PG_FUNCTION_ARGS)
  {
      inet       *src = PG_GETARG_INET_P(0);
!     char        tmp[sizeof("xxxx:xxxx:xxxx:xxxx:xxxx:xxxx:255.255.255.255/128")];
      char       *dst;
      int            len;

!     dst = inet_net_ntop(ip_family(src), ip_addr(src), ip_bits(src),
!                 tmp, sizeof(tmp));
!     if (dst == NULL)
!         elog(ERROR, "unable to print address (%s)", strerror(errno));
!     /* For CIDR, add /n if not present */
!     if (ip_type(src) && strchr(tmp, '/') == NULL)
      {
!         len = strlen(tmp);
!         snprintf(tmp + len, sizeof(tmp) - len, "/%u", ip_bits(src));
      }

      PG_RETURN_CSTRING(pstrdup(tmp));
  }
***************
*** 257,262 ****
--- 269,275 ----
      PG_RETURN_INET_P(network_in(str, type));
  }

+
  Datum
  text_cidr(PG_FUNCTION_ARGS)
  {
***************
*** 276,283 ****
      int            bits = PG_GETARG_INT32(1);
      inet       *dst;

!     if ((bits < 0) || (bits > 32))        /* no support for v6 yet */
!         elog(ERROR, "set_masklen - invalid value '%d'", bits);

      /* clone the original data */
      dst = (inet *) palloc(VARHDRSZ + sizeof(inet_struct));
--- 289,299 ----
      int            bits = PG_GETARG_INT32(1);
      inet       *dst;

!         if ( bits == -1 )
!             bits = ip_maxbits(src);
!
!     if ((bits < 0) || (bits > ip_maxbits(src)))
!             elog(ERROR, "set_masklen - invalid value '%d'", bits);

      /* clone the original data */
      dst = (inet *) palloc(VARHDRSZ + sizeof(inet_struct));
***************
*** 302,327 ****
  static int32
  network_cmp_internal(inet *a1, inet *a2)
  {
!     if (ip_family(a1) == AF_INET && ip_family(a2) == AF_INET)
      {
          int            order;

!         order = v4bitncmp(ip_v4addr(a1), ip_v4addr(a2),
!                           Min(ip_bits(a1), ip_bits(a2)));
          if (order != 0)
              return order;
          order = ((int) ip_bits(a1)) - ((int) ip_bits(a2));
          if (order != 0)
              return order;
!         return v4bitncmp(ip_v4addr(a1), ip_v4addr(a2), 32);
!     }
!     else
!     {
!         /* Go for an IPV6 address here, before faulting out: */
!         elog(ERROR, "cannot compare address families %d and %d",
!              ip_family(a1), ip_family(a2));
!         return 0;                /* keep compiler quiet */
      }
  }

  Datum
--- 318,338 ----
  static int32
  network_cmp_internal(inet *a1, inet *a2)
  {
!     if (ip_family(a1) == ip_family(a2))
      {
          int            order;

!         order = bitncmp(ip_addr(a1), ip_addr(a2),
!                 Min(ip_bits(a1), ip_bits(a2)));
          if (order != 0)
              return order;
          order = ((int) ip_bits(a1)) - ((int) ip_bits(a2));
          if (order != 0)
              return order;
!         return bitncmp(ip_addr(a1), ip_addr(a2), ip_maxbits(a1));
      }
+
+     return ip_family(a1) - ip_family(a2);
  }

  Datum
***************
*** 399,416 ****
      inet       *a1 = PG_GETARG_INET_P(0);
      inet       *a2 = PG_GETARG_INET_P(1);

!     if ((ip_family(a1) == AF_INET) && (ip_family(a2) == AF_INET))
      {
          PG_RETURN_BOOL(ip_bits(a1) > ip_bits(a2)
!            && v4bitncmp(ip_v4addr(a1), ip_v4addr(a2), ip_bits(a2)) == 0);
!     }
!     else
!     {
!         /* Go for an IPV6 address here, before faulting out: */
!         elog(ERROR, "cannot compare address families %d and %d",
!              ip_family(a1), ip_family(a2));
!         PG_RETURN_BOOL(false);
      }
  }

  Datum
--- 410,422 ----
      inet       *a1 = PG_GETARG_INET_P(0);
      inet       *a2 = PG_GETARG_INET_P(1);

!     if (ip_family(a1) == ip_family(a2))
      {
          PG_RETURN_BOOL(ip_bits(a1) > ip_bits(a2)
!            && bitncmp(ip_addr(a1), ip_addr(a2), ip_bits(a2)) == 0);
      }
+
+     PG_RETURN_BOOL(false);
  }

  Datum
***************
*** 419,436 ****
      inet       *a1 = PG_GETARG_INET_P(0);
      inet       *a2 = PG_GETARG_INET_P(1);

!     if ((ip_family(a1) == AF_INET) && (ip_family(a2) == AF_INET))
      {
          PG_RETURN_BOOL(ip_bits(a1) >= ip_bits(a2)
!            && v4bitncmp(ip_v4addr(a1), ip_v4addr(a2), ip_bits(a2)) == 0);
!     }
!     else
!     {
!         /* Go for an IPV6 address here, before faulting out: */
!         elog(ERROR, "cannot compare address families %d and %d",
!              ip_family(a1), ip_family(a2));
!         PG_RETURN_BOOL(false);
      }
  }

  Datum
--- 425,437 ----
      inet       *a1 = PG_GETARG_INET_P(0);
      inet       *a2 = PG_GETARG_INET_P(1);

!     if (ip_family(a1) == ip_family(a2))
      {
          PG_RETURN_BOOL(ip_bits(a1) >= ip_bits(a2)
!            && bitncmp(ip_addr(a1), ip_addr(a2), ip_bits(a2)) == 0);
      }
+
+     PG_RETURN_BOOL(false);
  }

  Datum
***************
*** 439,456 ****
      inet       *a1 = PG_GETARG_INET_P(0);
      inet       *a2 = PG_GETARG_INET_P(1);

!     if ((ip_family(a1) == AF_INET) && (ip_family(a2) == AF_INET))
      {
          PG_RETURN_BOOL(ip_bits(a1) < ip_bits(a2)
!            && v4bitncmp(ip_v4addr(a1), ip_v4addr(a2), ip_bits(a1)) == 0);
!     }
!     else
!     {
!         /* Go for an IPV6 address here, before faulting out: */
!         elog(ERROR, "cannot compare address families %d and %d",
!              ip_family(a1), ip_family(a2));
!         PG_RETURN_BOOL(false);
      }
  }

  Datum
--- 440,452 ----
      inet       *a1 = PG_GETARG_INET_P(0);
      inet       *a2 = PG_GETARG_INET_P(1);

!     if (ip_family(a1) == ip_family(a2))
      {
          PG_RETURN_BOOL(ip_bits(a1) < ip_bits(a2)
!            && bitncmp(ip_addr(a1), ip_addr(a2), ip_bits(a1)) == 0);
      }
+
+     PG_RETURN_BOOL(false);
  }

  Datum
***************
*** 459,476 ****
      inet       *a1 = PG_GETARG_INET_P(0);
      inet       *a2 = PG_GETARG_INET_P(1);

!     if ((ip_family(a1) == AF_INET) && (ip_family(a2) == AF_INET))
      {
          PG_RETURN_BOOL(ip_bits(a1) <= ip_bits(a2)
!            && v4bitncmp(ip_v4addr(a1), ip_v4addr(a2), ip_bits(a1)) == 0);
!     }
!     else
!     {
!         /* Go for an IPV6 address here, before faulting out: */
!         elog(ERROR, "cannot compare address families %d and %d",
!              ip_family(a1), ip_family(a2));
!         PG_RETURN_BOOL(false);
      }
  }

  /*
--- 455,467 ----
      inet       *a1 = PG_GETARG_INET_P(0);
      inet       *a2 = PG_GETARG_INET_P(1);

!     if (ip_family(a1) == ip_family(a2))
      {
          PG_RETURN_BOOL(ip_bits(a1) <= ip_bits(a2)
!            && bitncmp(ip_addr(a1), ip_addr(a2), ip_bits(a1)) == 0);
      }
+
+     PG_RETURN_BOOL(false);
  }

  /*
***************
*** 482,500 ****
      inet       *ip = PG_GETARG_INET_P(0);
      text       *ret;
      int            len;
!     char       *ptr,
!                 tmp[sizeof("255.255.255.255/32")];

!     if (ip_family(ip) == AF_INET)
!     {
!         /* It's an IP V4 address: */
!         /* force display of 32 bits, regardless of masklen... */
!         if (inet_net_ntop(AF_INET, &ip_v4addr(ip), 32, tmp, sizeof(tmp)) == NULL)
!             elog(ERROR, "unable to print host (%s)", strerror(errno));
!     }
!     else
!         /* Go for an IPV6 address here, before faulting out: */
!         elog(ERROR, "unknown address family (%d)", ip_family(ip));

      /* Suppress /n if present (shouldn't happen now) */
      if ((ptr = strchr(tmp, '/')) != NULL)
--- 473,485 ----
      inet       *ip = PG_GETARG_INET_P(0);
      text       *ret;
      int            len;
!     char       *ptr;
!     char        tmp[sizeof("xxxx:xxxx:xxxx:xxxx:xxxx:xxxx:255.255.255.255/128")];

!     /* force display of max bits, regardless of masklen... */
!     if (inet_net_ntop(ip_family(ip), ip_addr(ip), ip_maxbits(ip),
!               tmp, sizeof(tmp)) == NULL)
!         elog(ERROR, "unable to print host (%s)", strerror(errno));

      /* Suppress /n if present (shouldn't happen now) */
      if ((ptr = strchr(tmp, '/')) != NULL)
***************
*** 514,537 ****
      inet       *ip = PG_GETARG_INET_P(0);
      text       *ret;
      int            len;
!     char        tmp[sizeof("255.255.255.255/32")];

!     if (ip_family(ip) == AF_INET)
      {
!         /* It's an IP V4 address: */
!         /* force display of 32 bits, regardless of masklen... */
!         if (inet_net_ntop(AF_INET, &ip_v4addr(ip), 32, tmp, sizeof(tmp)) == NULL)
!             elog(ERROR, "unable to print host (%s)", strerror(errno));
!         /* Add /n if not present (which it won't be) */
!         if (strchr(tmp, '/') == NULL)
!         {
!             len = strlen(tmp);
!             snprintf(tmp + len, sizeof(tmp) - len, "/%u", ip_bits(ip));
!         }
      }
-     else
-         /* Go for an IPV6 address here, before faulting out: */
-         elog(ERROR, "unknown address family (%d)", ip_family(ip));

      /* Return string as a text datum */
      len = strlen(tmp);
--- 499,515 ----
      inet       *ip = PG_GETARG_INET_P(0);
      text       *ret;
      int            len;
!     char        tmp[sizeof("xxxx:xxxx:xxxx:xxxx:xxxx:xxxx:255.255.255.255/128")];

!     if (inet_net_ntop(ip_family(ip), ip_addr(ip), ip_maxbits(ip),
!               tmp, sizeof(tmp)) == NULL)
!         elog(ERROR, "unable to print host (%s)", strerror(errno));
!     /* Add /n if not present (which it won't be) */
!     if (strchr(tmp, '/') == NULL)
      {
!         len = strlen(tmp);
!         snprintf(tmp + len, sizeof(tmp) - len, "/%u", ip_bits(ip));
      }

      /* Return string as a text datum */
      len = strlen(tmp);
***************
*** 548,571 ****
      text       *ret;
      char       *dst;
      int            len;
!     char        tmp[sizeof("255.255.255.255/32")];

!     if (ip_family(ip) == AF_INET)
!     {
!         /* It's an IP V4 address: */
!         if (ip_type(ip))
!             dst = inet_cidr_ntop(AF_INET, &ip_v4addr(ip), ip_bits(ip),
!                                  tmp, sizeof(tmp));
!         else
!             dst = inet_net_ntop(AF_INET, &ip_v4addr(ip), ip_bits(ip),
!                                 tmp, sizeof(tmp));
!
!         if (dst == NULL)
!             elog(ERROR, "unable to print address (%s)", strerror(errno));
!     }
!     else
!         /* Go for an IPV6 address here, before faulting out: */
!         elog(ERROR, "unknown address family (%d)", ip_family(ip));

      /* Return string as a text datum */
      len = strlen(tmp);
--- 526,543 ----
      text       *ret;
      char       *dst;
      int            len;
!     char        tmp[sizeof("xxxx:xxxx:xxxx:xxxx:xxxx:xxxx:255.255.255.255/128")];

!     if (ip_type(ip))
!         dst = inet_cidr_ntop(ip_family(ip), ip_addr(ip),
!                      ip_bits(ip), tmp, sizeof(tmp));
!     else
!         dst = inet_net_ntop(ip_family(ip), ip_addr(ip),
!                     ip_bits(ip), tmp, sizeof(tmp));
!
!     if (dst == NULL)
!         elog(ERROR, "unable to print address (%s)",
!              strerror(errno));

      /* Return string as a text datum */
      len = strlen(tmp);
***************
*** 584,622 ****
  }

  Datum
  network_broadcast(PG_FUNCTION_ARGS)
  {
      inet       *ip = PG_GETARG_INET_P(0);
      inet       *dst;

      /* make sure any unused bits are zeroed */
      dst = (inet *) palloc0(VARHDRSZ + sizeof(inet_struct));

!     if (ip_family(ip) == AF_INET)
!     {
!         /* It's an IP V4 address: */
!         unsigned long mask = 0xffffffff;
!
!         /*
!          * Shifting by 32 or more bits does not yield portable results, so
!          * don't try it.
!          */
!         if (ip_bits(ip) < 32)
!             mask >>= ip_bits(ip);
!         else
!             mask = 0;
!
!         ip_v4addr(dst) = htonl(ntohl(ip_v4addr(ip)) | mask);
      }
-     else
-         /* Go for an IPV6 address here, before faulting out: */
-         elog(ERROR, "unknown address family (%d)", ip_family(ip));

      ip_family(dst) = ip_family(ip);
      ip_bits(dst) = ip_bits(ip);
      ip_type(dst) = 0;
      VARATT_SIZEP(dst) = VARHDRSZ
!         + ((char *) &ip_v4addr(dst) - (char *) VARDATA(dst))
          + ip_addrsize(dst);

      PG_RETURN_INET_P(dst);
--- 556,621 ----
  }

  Datum
+ network_family(PG_FUNCTION_ARGS)
+ {
+     inet       *ip = PG_GETARG_INET_P(0);
+
+     switch (ip_family(ip)) {
+     case PGSQL_AF_INET:
+         PG_RETURN_INT32(4);
+         break;
+     case PGSQL_AF_INET6:
+         PG_RETURN_INT32(6);
+         break;
+     default:
+         PG_RETURN_INT32(0);
+         break;
+     }
+ }
+
+ Datum
  network_broadcast(PG_FUNCTION_ARGS)
  {
      inet       *ip = PG_GETARG_INET_P(0);
      inet       *dst;
+     int byte;
+     int bits;
+     int maxbytes;
+     unsigned char mask;
+     unsigned char *a, *b;

      /* make sure any unused bits are zeroed */
      dst = (inet *) palloc0(VARHDRSZ + sizeof(inet_struct));

!     if (ip_family(ip) == PGSQL_AF_INET) {
!         maxbytes = 4;
!     } else {
!         maxbytes = 16;
!     }
!
!     bits = ip_bits(ip);
!     a = ip_addr(ip);
!     b = ip_addr(dst);
!
!     for (byte = 0 ; byte < maxbytes ; byte++) {
!         if (bits >= 8) {
!             mask = 0x00;
!             bits -= 8;
!         } else if (bits == 0) {
!             mask = 0xff;
!         } else {
!             mask = 0xff >> bits;
!             bits = 0;
!         }
!
!         b[byte] = a[byte] | mask;
      }

      ip_family(dst) = ip_family(ip);
      ip_bits(dst) = ip_bits(ip);
      ip_type(dst) = 0;
      VARATT_SIZEP(dst) = VARHDRSZ
!         + ((char *)ip_addr(dst) - (char *)VARDATA(dst))
          + ip_addrsize(dst);

      PG_RETURN_INET_P(dst);
***************
*** 627,661 ****
  {
      inet       *ip = PG_GETARG_INET_P(0);
      inet       *dst;

      /* make sure any unused bits are zeroed */
      dst = (inet *) palloc0(VARHDRSZ + sizeof(inet_struct));

!     if (ip_family(ip) == AF_INET)
!     {
!         /* It's an IP V4 address: */
!         unsigned long mask = 0xffffffff;
!
!         /*
!          * Shifting by 32 or more bits does not yield portable results, so
!          * don't try it.
!          */
!         if (ip_bits(ip) > 0)
!             mask <<= (32 - ip_bits(ip));
!         else
!             mask = 0;
!
!         ip_v4addr(dst) = htonl(ntohl(ip_v4addr(ip)) & mask);
      }
-     else
-         /* Go for an IPV6 address here, before faulting out: */
-         elog(ERROR, "unknown address family (%d)", ip_family(ip));

      ip_family(dst) = ip_family(ip);
      ip_bits(dst) = ip_bits(ip);
      ip_type(dst) = 1;
      VARATT_SIZEP(dst) = VARHDRSZ
!         + ((char *) &ip_v4addr(dst) - (char *) VARDATA(dst))
          + ip_addrsize(dst);

      PG_RETURN_INET_P(dst);
--- 626,669 ----
  {
      inet       *ip = PG_GETARG_INET_P(0);
      inet       *dst;
+     int byte;
+     int bits;
+     int maxbytes;
+     unsigned char mask;
+     unsigned char *a, *b;

      /* make sure any unused bits are zeroed */
      dst = (inet *) palloc0(VARHDRSZ + sizeof(inet_struct));

!     if (ip_family(ip) == PGSQL_AF_INET) {
!         maxbytes = 4;
!     } else {
!         maxbytes = 16;
!     }
!
!     bits = ip_bits(ip);
!     a = ip_addr(ip);
!     b = ip_addr(dst);
!
!     byte = 0;
!     while (bits) {
!         if (bits >= 8) {
!             mask = 0xff;
!             bits -= 8;
!         } else {
!             mask = 0xff << (8 - bits);
!             bits = 0;
!         }
!
!         b[byte] = a[byte] & mask;
!         byte++;
      }

      ip_family(dst) = ip_family(ip);
      ip_bits(dst) = ip_bits(ip);
      ip_type(dst) = 1;
      VARATT_SIZEP(dst) = VARHDRSZ
!         + ((char *)ip_addr(dst) - (char *)VARDATA(dst))
          + ip_addrsize(dst);

      PG_RETURN_INET_P(dst);
***************
*** 666,701 ****
  {
      inet       *ip = PG_GETARG_INET_P(0);
      inet       *dst;

      /* make sure any unused bits are zeroed */
      dst = (inet *) palloc0(VARHDRSZ + sizeof(inet_struct));

!     if (ip_family(ip) == AF_INET)
!     {
!         /* It's an IP V4 address: */
!         unsigned long mask = 0xffffffff;
!
!         /*
!          * Shifting by 32 or more bits does not yield portable results, so
!          * don't try it.
!          */
!         if (ip_bits(ip) > 0)
!             mask <<= (32 - ip_bits(ip));
!         else
!             mask = 0;
!
!         ip_v4addr(dst) = htonl(mask);
!
!         ip_bits(dst) = 32;
      }
-     else
-         /* Go for an IPV6 address here, before faulting out: */
-         elog(ERROR, "unknown address family (%d)", ip_family(ip));

      ip_family(dst) = ip_family(ip);
      ip_type(dst) = 0;
      VARATT_SIZEP(dst) = VARHDRSZ
!         + ((char *) &ip_v4addr(dst) - (char *) VARDATA(dst))
          + ip_addrsize(dst);

      PG_RETURN_INET_P(dst);
--- 674,716 ----
  {
      inet       *ip = PG_GETARG_INET_P(0);
      inet       *dst;
+     int byte;
+     int bits;
+     int maxbytes;
+     unsigned char mask;
+     unsigned char *b;

      /* make sure any unused bits are zeroed */
      dst = (inet *) palloc0(VARHDRSZ + sizeof(inet_struct));

!     if (ip_family(ip) == PGSQL_AF_INET) {
!         maxbytes = 4;
!     } else {
!         maxbytes = 16;
!     }
!
!     bits = ip_bits(ip);
!     b = ip_addr(dst);
!
!     byte = 0;
!     while (bits) {
!         if (bits >= 8) {
!             mask = 0xff;
!             bits -= 8;
!         } else {
!             mask = 0xff << (8 - bits);
!             bits = 0;
!         }
!
!         b[byte] = mask;
!         byte++;
      }

      ip_family(dst) = ip_family(ip);
+     ip_bits(dst) = ip_bits(ip);
      ip_type(dst) = 0;
      VARATT_SIZEP(dst) = VARHDRSZ
!         + ((char *)ip_addr(dst) - (char *)VARDATA(dst))
          + ip_addrsize(dst);

      PG_RETURN_INET_P(dst);
***************
*** 760,771 ****
          case CIDROID:
              {
                  inet       *ip = DatumGetInetP(value);
!
!                 if (ip_family(ip) == AF_INET)
!                     return (double) ip_v4addr(ip);
                  else
!                     /* Go for an IPV6 address here, before faulting out: */
!                     elog(ERROR, "unknown address family (%d)", ip_family(ip));
                  break;
              }
          case MACADDROID:
--- 775,800 ----
          case CIDROID:
              {
                  inet       *ip = DatumGetInetP(value);
!                 int len;
!                 double res;
!                 int i;
!
!                 /*
!                  * Note that we don't use the full address
!                  * here.
!                  */
!                 if (ip_family(ip) == PGSQL_AF_INET)
!                     len = 4;
                  else
!                     len = 5;
!
!                 res = ip_family(ip);
!                 for (i = 0 ; i < len ; i++) {
!                     res *= 256;
!                     res += ip_addr(ip)[i];
!                 }
!                 return res;
!
                  break;
              }
          case MACADDROID:
***************
*** 788,840 ****
      return 0;
  }

-
  /*
!  *    Bitwise comparison for V4 addresses.  Add V6 implementation!
   */
-
  static int
! v4bitncmp(unsigned long a1, unsigned long a2, int bits)
  {
!     unsigned long mask;

!     /*
!      * Shifting by 32 or more bits does not yield portable results, so
!      * don't try it.
!      */
!     if (bits > 0)
!         mask = (0xFFFFFFFFL << (32 - bits)) & 0xFFFFFFFFL;
!     else
!         mask = 0;
!     a1 = ntohl(a1);
!     a2 = ntohl(a2);
!     if ((a1 & mask) < (a2 & mask))
!         return (-1);
!     else if ((a1 & mask) > (a2 & mask))
!         return (1);
      return (0);
  }

- /*
-  * Returns true if given address fits fully within the specified bit width.
-  */
  static bool
! v4addressOK(unsigned long a1, int bits)
  {
!     unsigned long mask;

!     /*
!      * Shifting by 32 or more bits does not yield portable results, so
!      * don't try it.
!      */
!     if (bits > 0)
!         mask = (0xFFFFFFFFL << (32 - bits)) & 0xFFFFFFFFL;
!     else
!         mask = 0;
!     a1 = ntohl(a1);
!     if ((a1 & mask) == a1)
!         return true;
!     return false;
  }


--- 817,894 ----
      return 0;
  }

  /*
!  * int
!  * bitncmp(l, r, n)
!  *      compare bit masks l and r, for n bits.
!  * return:
!  *      -1, 1, or 0 in the libc tradition.
!  * note:
!  *      network byte order assumed.  this means 192.5.5.240/28 has
!  *      0x11110000 in its fourth octet.
!  * author:
!  *      Paul Vixie (ISC), June 1996
   */
  static int
! bitncmp(void *l, void *r, int n)
  {
!     u_int lb, rb;
!     int x, b;

!     b = n / 8;
!     x = memcmp(l, r, b);
!     if (x)
!         return (x);
!
!     lb = ((const u_char *)l)[b];
!     rb = ((const u_char *)r)[b];
!     for (b = n % 8; b > 0; b--) {
!         if ((lb & 0x80) != (rb & 0x80)) {
!             if (lb & 0x80)
!                 return (1);
!             return (-1);
!         }
!         lb <<= 1;
!         rb <<= 1;
!     }
      return (0);
  }

  static bool
! addressOK(unsigned char *a, int bits, int family)
  {
!     int byte;
!     int nbits;
!     int maxbits;
!     int maxbytes;
!     unsigned char mask;
!
!     if (family == PGSQL_AF_INET) {
!         maxbits = 32;
!         maxbytes = 4;
!     } else {
!         maxbits = 128;
!         maxbytes = 16;
!     }
!     assert(bits <= maxbits);
!
!     if (bits == maxbits)
!         return 1;
!
!     byte = (bits + 7) / 8;
!     nbits = bits % 8;
!     mask = 0xff;
!     if (bits != 0)
!         mask >>= nbits;
!
!     while (byte < maxbytes) {
!         if ((a[byte] & mask) != 0)
!             return 0;
!         mask = 0xff;
!         byte++;
!     }

!     return 1;
  }


***************
*** 852,866 ****

  /*
   * return "last" IP on a given network. It's the broadcast address,
!  * however, masklen has to be set to 32, since
   * 192.168.0.255/24 is considered less than 192.168.0.255/32
   *
!  * NB: this is not IPv6 ready ...
   */
  Datum
  network_scan_last(Datum in)
  {
      return DirectFunctionCall2(inet_set_masklen,
                                 DirectFunctionCall1(network_broadcast, in),
!                                Int32GetDatum(32));
  }
--- 906,921 ----

  /*
   * return "last" IP on a given network. It's the broadcast address,
!  * however, masklen has to be set to its max btis, since
   * 192.168.0.255/24 is considered less than 192.168.0.255/32
   *
!  * inet_set_masklen() hacked to max out the masklength to 128 for IPv6
!  * and 32 for IPv4 when given '-1' as argument.
   */
  Datum
  network_scan_last(Datum in)
  {
      return DirectFunctionCall2(inet_set_masklen,
                                 DirectFunctionCall1(network_broadcast, in),
!                Int32GetDatum(-1));
  }
Index: src/include/catalog/catversion.h
===================================================================
RCS file: /cvsroot/pgsql-server/src/include/catalog/catversion.h,v
retrieving revision 1.198
diff -c -c -r1.198 catversion.h
*** src/include/catalog/catversion.h    6 Jun 2003 15:04:02 -0000    1.198
--- src/include/catalog/catversion.h    12 Jun 2003 05:13:20 -0000
***************
*** 53,58 ****
   */

  /*                            yyyymmddN */
! #define CATALOG_VERSION_NO    200306051

  #endif
--- 53,58 ----
   */

  /*                            yyyymmddN */
! #define CATALOG_VERSION_NO    200306121

  #endif
Index: src/include/catalog/pg_proc.h
===================================================================
RCS file: /cvsroot/pgsql-server/src/include/catalog/pg_proc.h,v
retrieving revision 1.303
diff -c -c -r1.303 pg_proc.h
*** src/include/catalog/pg_proc.h    11 Jun 2003 09:23:55 -0000    1.303
--- src/include/catalog/pg_proc.h    12 Jun 2003 05:13:24 -0000
***************
*** 2377,2382 ****
--- 2377,2384 ----
  DESCR("show address octets only");
  DATA(insert OID = 730 (  text                PGNSP PGUID 12 f f t f i 1 25 "869"  network_show - _null_ ));
  DESCR("show all parts of inet/cidr value");
+ DATA(insert OID = 715 (  family            PGNSP PGUID 12 f f t f i 1 23 "869"  network_family - _null_ ));
+ DESCR("return address family (4 for IPv4, 6 for IPv6)");
  DATA(insert OID = 1362 (  hostmask            PGNSP PGUID 12 f f t f i 1 869 "869"    network_hostmask - _null_ ));
  DESCR("hostmask of address");
  DATA(insert OID = 1713 (  inet                PGNSP PGUID 12 f f t f i 1 869 "25"  text_inet - _null_ ));
Index: src/include/utils/builtins.h
===================================================================
RCS file: /cvsroot/pgsql-server/src/include/utils/builtins.h,v
retrieving revision 1.219
diff -c -c -r1.219 builtins.h
*** src/include/utils/builtins.h    26 May 2003 00:11:28 -0000    1.219
--- src/include/utils/builtins.h    12 Jun 2003 05:13:25 -0000
***************
*** 644,649 ****
--- 644,650 ----
  extern Datum network_netmask(PG_FUNCTION_ARGS);
  extern Datum network_hostmask(PG_FUNCTION_ARGS);
  extern Datum network_masklen(PG_FUNCTION_ARGS);
+ extern Datum network_family(PG_FUNCTION_ARGS);
  extern Datum network_broadcast(PG_FUNCTION_ARGS);
  extern Datum network_host(PG_FUNCTION_ARGS);
  extern Datum network_show(PG_FUNCTION_ARGS);
Index: src/include/utils/inet.h
===================================================================
RCS file: /cvsroot/pgsql-server/src/include/utils/inet.h,v
retrieving revision 1.13
diff -c -c -r1.13 inet.h
*** src/include/utils/inet.h    20 Jun 2002 20:29:53 -0000    1.13
--- src/include/utils/inet.h    12 Jun 2003 05:13:25 -0000
***************
*** 23,34 ****
      unsigned char family;
      unsigned char bits;
      unsigned char type;
!     union
!     {
!         unsigned int ipv4_addr; /* network byte order */
!         /* add IPV6 address type here */
!     }            addr;
  } inet_struct;

  /*
   * Both INET and CIDR addresses are represented within Postgres as varlena
--- 23,40 ----
      unsigned char family;
      unsigned char bits;
      unsigned char type;
!     unsigned char ip_addr[16]; /* 128 bits of address */
  } inet_struct;
+
+ /*
+  * Referencing all of the non-AF_INET types to AF_INET lets us work on
+  * machines which may not have the appropriate address family (like
+  * inet6 addresses when AF_INET6 isn't present) but doesn't cause a
+  * dump/reload requirement.  Existing databases used AF_INET for the family
+  * type on disk.
+  */
+ #define PGSQL_AF_INET    (AF_INET + 0)
+ #define PGSQL_AF_INET6    (AF_INET + 1)

  /*
   * Both INET and CIDR addresses are represented within Postgres as varlena
Index: src/test/regress/expected/inet.out
===================================================================
RCS file: /cvsroot/pgsql-server/src/test/regress/expected/inet.out,v
retrieving revision 1.15
diff -c -c -r1.15 inet.out
*** src/test/regress/expected/inet.out    15 Jan 2003 20:01:01 -0000    1.15
--- src/test/regress/expected/inet.out    12 Jun 2003 05:13:26 -0000
***************
*** 19,128 ****
  INSERT INTO INET_TBL (c, i) VALUES ('10', '10.1.2.3/8');
  INSERT INTO INET_TBL (c, i) VALUES ('10', '11.1.2.3/8');
  INSERT INTO INET_TBL (c, i) VALUES ('10', '9.1.2.3/8');
  -- check that CIDR rejects invalid input:
  INSERT INTO INET_TBL (c, i) VALUES ('192.168.1.2/24', '192.168.1.226');
  ERROR:  invalid CIDR value '192.168.1.2/24': has bits set to right of mask
  -- check that CIDR rejects invalid input when converting from text:
  INSERT INTO INET_TBL (c, i) VALUES (cidr('192.168.1.2/24'), '192.168.1.226');
  ERROR:  invalid CIDR value '192.168.1.2/24': has bits set to right of mask
  SELECT '' AS ten, c AS cidr, i AS inet FROM INET_TBL;
!  ten |      cidr      |       inet
! -----+----------------+------------------
!      | 192.168.1.0/24 | 192.168.1.226/24
!      | 192.168.1.0/24 | 192.168.1.226
!      | 192.168.1.0/24 | 192.168.1.0/24
!      | 192.168.1.0/24 | 192.168.1.0/25
!      | 192.168.1.0/24 | 192.168.1.255/24
!      | 192.168.1.0/24 | 192.168.1.255/25
!      | 10.0.0.0/8     | 10.1.2.3/8
!      | 10.0.0.0/32    | 10.1.2.3/8
!      | 10.1.2.3/32    | 10.1.2.3
!      | 10.1.2.0/24    | 10.1.2.3/24
!      | 10.1.0.0/16    | 10.1.2.3/16
!      | 10.0.0.0/8     | 10.1.2.3/8
!      | 10.0.0.0/8     | 11.1.2.3/8
!      | 10.0.0.0/8     | 9.1.2.3/8
! (14 rows)

  -- now test some support functions
! SELECT '' AS ten, i AS inet, host(i), text(i) FROM INET_TBL;
!  ten |       inet       |     host      |       text
! -----+------------------+---------------+------------------
!      | 192.168.1.226/24 | 192.168.1.226 | 192.168.1.226/24
!      | 192.168.1.226    | 192.168.1.226 | 192.168.1.226/32
!      | 192.168.1.0/24   | 192.168.1.0   | 192.168.1.0/24
!      | 192.168.1.0/25   | 192.168.1.0   | 192.168.1.0/25
!      | 192.168.1.255/24 | 192.168.1.255 | 192.168.1.255/24
!      | 192.168.1.255/25 | 192.168.1.255 | 192.168.1.255/25
!      | 10.1.2.3/8       | 10.1.2.3      | 10.1.2.3/8
!      | 10.1.2.3/8       | 10.1.2.3      | 10.1.2.3/8
!      | 10.1.2.3         | 10.1.2.3      | 10.1.2.3/32
!      | 10.1.2.3/24      | 10.1.2.3      | 10.1.2.3/24
!      | 10.1.2.3/16      | 10.1.2.3      | 10.1.2.3/16
!      | 10.1.2.3/8       | 10.1.2.3      | 10.1.2.3/8
!      | 11.1.2.3/8       | 11.1.2.3      | 11.1.2.3/8
!      | 9.1.2.3/8        | 9.1.2.3       | 9.1.2.3/8
! (14 rows)

  SELECT '' AS ten, c AS cidr, broadcast(c),
    i AS inet, broadcast(i) FROM INET_TBL;
!  ten |      cidr      |    broadcast     |       inet       |    broadcast
! -----+----------------+------------------+------------------+------------------
!      | 192.168.1.0/24 | 192.168.1.255/24 | 192.168.1.226/24 | 192.168.1.255/24
!      | 192.168.1.0/24 | 192.168.1.255/24 | 192.168.1.226    | 192.168.1.226
!      | 192.168.1.0/24 | 192.168.1.255/24 | 192.168.1.0/24   | 192.168.1.255/24
!      | 192.168.1.0/24 | 192.168.1.255/24 | 192.168.1.0/25   | 192.168.1.127/25
!      | 192.168.1.0/24 | 192.168.1.255/24 | 192.168.1.255/24 | 192.168.1.255/24
!      | 192.168.1.0/24 | 192.168.1.255/24 | 192.168.1.255/25 | 192.168.1.255/25
!      | 10.0.0.0/8     | 10.255.255.255/8 | 10.1.2.3/8       | 10.255.255.255/8
!      | 10.0.0.0/32    | 10.0.0.0         | 10.1.2.3/8       | 10.255.255.255/8
!      | 10.1.2.3/32    | 10.1.2.3         | 10.1.2.3         | 10.1.2.3
!      | 10.1.2.0/24    | 10.1.2.255/24    | 10.1.2.3/24      | 10.1.2.255/24
!      | 10.1.0.0/16    | 10.1.255.255/16  | 10.1.2.3/16      | 10.1.255.255/16
!      | 10.0.0.0/8     | 10.255.255.255/8 | 10.1.2.3/8       | 10.255.255.255/8
!      | 10.0.0.0/8     | 10.255.255.255/8 | 11.1.2.3/8       | 11.255.255.255/8
!      | 10.0.0.0/8     | 10.255.255.255/8 | 9.1.2.3/8        | 9.255.255.255/8
! (14 rows)

  SELECT '' AS ten, c AS cidr, network(c) AS "network(cidr)",
    i AS inet, network(i) AS "network(inet)" FROM INET_TBL;
!  ten |      cidr      | network(cidr)  |       inet       |  network(inet)
! -----+----------------+----------------+------------------+------------------
!      | 192.168.1.0/24 | 192.168.1.0/24 | 192.168.1.226/24 | 192.168.1.0/24
!      | 192.168.1.0/24 | 192.168.1.0/24 | 192.168.1.226    | 192.168.1.226/32
!      | 192.168.1.0/24 | 192.168.1.0/24 | 192.168.1.0/24   | 192.168.1.0/24
!      | 192.168.1.0/24 | 192.168.1.0/24 | 192.168.1.0/25   | 192.168.1.0/25
!      | 192.168.1.0/24 | 192.168.1.0/24 | 192.168.1.255/24 | 192.168.1.0/24
!      | 192.168.1.0/24 | 192.168.1.0/24 | 192.168.1.255/25 | 192.168.1.128/25
!      | 10.0.0.0/8     | 10.0.0.0/8     | 10.1.2.3/8       | 10.0.0.0/8
!      | 10.0.0.0/32    | 10.0.0.0/32    | 10.1.2.3/8       | 10.0.0.0/8
!      | 10.1.2.3/32    | 10.1.2.3/32    | 10.1.2.3         | 10.1.2.3/32
!      | 10.1.2.0/24    | 10.1.2.0/24    | 10.1.2.3/24      | 10.1.2.0/24
!      | 10.1.0.0/16    | 10.1.0.0/16    | 10.1.2.3/16      | 10.1.0.0/16
!      | 10.0.0.0/8     | 10.0.0.0/8     | 10.1.2.3/8       | 10.0.0.0/8
!      | 10.0.0.0/8     | 10.0.0.0/8     | 11.1.2.3/8       | 11.0.0.0/8
!      | 10.0.0.0/8     | 10.0.0.0/8     | 9.1.2.3/8        | 9.0.0.0/8
! (14 rows)

  SELECT '' AS ten, c AS cidr, masklen(c) AS "masklen(cidr)",
    i AS inet, masklen(i) AS "masklen(inet)" FROM INET_TBL;
!  ten |      cidr      | masklen(cidr) |       inet       | masklen(inet)
! -----+----------------+---------------+------------------+---------------
!      | 192.168.1.0/24 |            24 | 192.168.1.226/24 |            24
!      | 192.168.1.0/24 |            24 | 192.168.1.226    |            32
!      | 192.168.1.0/24 |            24 | 192.168.1.0/24   |            24
!      | 192.168.1.0/24 |            24 | 192.168.1.0/25   |            25
!      | 192.168.1.0/24 |            24 | 192.168.1.255/24 |            24
!      | 192.168.1.0/24 |            24 | 192.168.1.255/25 |            25
!      | 10.0.0.0/8     |             8 | 10.1.2.3/8       |             8
!      | 10.0.0.0/32    |            32 | 10.1.2.3/8       |             8
!      | 10.1.2.3/32    |            32 | 10.1.2.3         |            32
!      | 10.1.2.0/24    |            24 | 10.1.2.3/24      |            24
!      | 10.1.0.0/16    |            16 | 10.1.2.3/16      |            16
!      | 10.0.0.0/8     |             8 | 10.1.2.3/8       |             8
!      | 10.0.0.0/8     |             8 | 11.1.2.3/8       |             8
!      | 10.0.0.0/8     |             8 | 9.1.2.3/8        |             8
! (14 rows)

  SELECT '' AS four, c AS cidr, masklen(c) AS "masklen(cidr)",
    i AS inet, masklen(i) AS "masklen(inet)" FROM INET_TBL
--- 19,150 ----
  INSERT INTO INET_TBL (c, i) VALUES ('10', '10.1.2.3/8');
  INSERT INTO INET_TBL (c, i) VALUES ('10', '11.1.2.3/8');
  INSERT INTO INET_TBL (c, i) VALUES ('10', '9.1.2.3/8');
+ INSERT INTO INET_TBL (c, i) VALUES ('10:23::f1', '10:23::f1/64');
+ INSERT INTO INET_TBL (c, i) VALUES ('10:23::8000/113', '10:23::ffff');
+ INSERT INTO INET_TBL (c, i) VALUES ('::ffff:1.2.3.4', '::4.3.2.1/24');
  -- check that CIDR rejects invalid input:
  INSERT INTO INET_TBL (c, i) VALUES ('192.168.1.2/24', '192.168.1.226');
  ERROR:  invalid CIDR value '192.168.1.2/24': has bits set to right of mask
+ INSERT INTO INET_TBL (c, i) VALUES ('1234::1234::1234', '::1.2.3.4');
+ ERROR:  invalid CIDR value '1234::1234::1234'
  -- check that CIDR rejects invalid input when converting from text:
  INSERT INTO INET_TBL (c, i) VALUES (cidr('192.168.1.2/24'), '192.168.1.226');
  ERROR:  invalid CIDR value '192.168.1.2/24': has bits set to right of mask
+ INSERT INTO INET_TBL (c, i) VALUES (cidr('ffff:ffff:ffff:ffff::/24'), '::192.168.1.226');
+ ERROR:  invalid CIDR value 'ffff:ffff:ffff:ffff::/24': has bits set to right of mask
  SELECT '' AS ten, c AS cidr, i AS inet FROM INET_TBL;
!  ten |      cidr          |       inet
! -----+--------------------+------------------
!      | 192.168.1.0/24     | 192.168.1.226/24
!      | 192.168.1.0/24     | 192.168.1.226
!      | 192.168.1.0/24     | 192.168.1.0/24
!      | 192.168.1.0/24     | 192.168.1.0/25
!      | 192.168.1.0/24     | 192.168.1.255/24
!      | 192.168.1.0/24     | 192.168.1.255/25
!      | 10.0.0.0/8         | 10.1.2.3/8
!      | 10.0.0.0/32        | 10.1.2.3/8
!      | 10.1.2.3/32        | 10.1.2.3
!      | 10.1.2.0/24        | 10.1.2.3/24
!      | 10.1.0.0/16        | 10.1.2.3/16
!      | 10.0.0.0/8         | 10.1.2.3/8
!      | 10.0.0.0/8         | 11.1.2.3/8
!      | 10.0.0.0/8         | 9.1.2.3/8
!      | 10:23::f1/128      | 10:23::f1/64
!      | 10:23::8000/113    | 10:23::ffff
!      | ::ffff:1.2.3.4/128 | ::4.3.2.1/24
! (17 rows)

  -- now test some support functions
! SELECT '' AS ten, i AS inet, host(i), text(i), family(i) FROM INET_TBL;
!  ten |       inet       |     host      |       text       | family
! -----+------------------+---------------+------------------+--------
!      | 192.168.1.226/24 | 192.168.1.226 | 192.168.1.226/24 |      4
!      | 192.168.1.226    | 192.168.1.226 | 192.168.1.226/32 |      4
!      | 192.168.1.0/24   | 192.168.1.0   | 192.168.1.0/24   |      4
!      | 192.168.1.0/25   | 192.168.1.0   | 192.168.1.0/25   |      4
!      | 192.168.1.255/24 | 192.168.1.255 | 192.168.1.255/24 |      4
!      | 192.168.1.255/25 | 192.168.1.255 | 192.168.1.255/25 |      4
!      | 10.1.2.3/8       | 10.1.2.3      | 10.1.2.3/8       |      4
!      | 10.1.2.3/8       | 10.1.2.3      | 10.1.2.3/8       |      4
!      | 10.1.2.3         | 10.1.2.3      | 10.1.2.3/32      |      4
!      | 10.1.2.3/24      | 10.1.2.3      | 10.1.2.3/24      |      4
!      | 10.1.2.3/16      | 10.1.2.3      | 10.1.2.3/16      |      4
!      | 10.1.2.3/8       | 10.1.2.3      | 10.1.2.3/8       |      4
!      | 11.1.2.3/8       | 11.1.2.3      | 11.1.2.3/8       |      4
!      | 9.1.2.3/8        | 9.1.2.3       | 9.1.2.3/8        |      4
!      | 10:23::f1/64     | 10:23::f1     | 10:23::f1/64     |      6
!      | 10:23::ffff      | 10:23::ffff   | 10:23::ffff/128  |      6
!      | ::4.3.2.1/24     | ::4.3.2.1     | ::4.3.2.1/24     |      6
! (17 rows)

  SELECT '' AS ten, c AS cidr, broadcast(c),
    i AS inet, broadcast(i) FROM INET_TBL;
!  ten |      cidr          |    broadcast     |       inet       |               broadcast
! -----+--------------------+------------------+------------------+---------------------------------------
!      | 192.168.1.0/24     | 192.168.1.255/24 | 192.168.1.226/24 | 192.168.1.255/24
!      | 192.168.1.0/24     | 192.168.1.255/24 | 192.168.1.226    | 192.168.1.226
!      | 192.168.1.0/24     | 192.168.1.255/24 | 192.168.1.0/24   | 192.168.1.255/24
!      | 192.168.1.0/24     | 192.168.1.255/24 | 192.168.1.0/25   | 192.168.1.127/25
!      | 192.168.1.0/24     | 192.168.1.255/24 | 192.168.1.255/24 | 192.168.1.255/24
!      | 192.168.1.0/24     | 192.168.1.255/24 | 192.168.1.255/25 | 192.168.1.255/25
!      | 10.0.0.0/8         | 10.255.255.255/8 | 10.1.2.3/8       | 10.255.255.255/8
!      | 10.0.0.0/32        | 10.0.0.0         | 10.1.2.3/8       | 10.255.255.255/8
!      | 10.1.2.3/32        | 10.1.2.3         | 10.1.2.3         | 10.1.2.3
!      | 10.1.2.0/24        | 10.1.2.255/24    | 10.1.2.3/24      | 10.1.2.255/24
!      | 10.1.0.0/16        | 10.1.255.255/16  | 10.1.2.3/16      | 10.1.255.255/16
!      | 10.0.0.0/8         | 10.255.255.255/8 | 10.1.2.3/8       | 10.255.255.255/8
!      | 10.0.0.0/8         | 10.255.255.255/8 | 11.1.2.3/8       | 11.255.255.255/8
!      | 10.0.0.0/8         | 10.255.255.255/8 | 9.1.2.3/8        | 9.255.255.255/8
!      | 10:23::f1/128      | 10:23::f1        | 10:23::f1/64     | 10:23::ffff:ffff:ffff:ffff/64
!      | 10:23::8000/113    | 10:23::ffff/113  | 10:23::ffff      | 10:23::ffff
!      | ::ffff:1.2.3.4/128 | ::ffff:1.2.3.4   | ::4.3.2.1/24     | 0:ff:ffff:ffff:ffff:ffff:ffff:ffff/24
! (17 rows)

  SELECT '' AS ten, c AS cidr, network(c) AS "network(cidr)",
    i AS inet, network(i) AS "network(inet)" FROM INET_TBL;
!  ten |        cidr        |  network(cidr)     |       inet       |  network(inet)
! -----+--------------------+--------------------+------------------+------------------
!      | 192.168.1.0/24     | 192.168.1.0/24     | 192.168.1.226/24 | 192.168.1.0/24
!      | 192.168.1.0/24     | 192.168.1.0/24     | 192.168.1.226    | 192.168.1.226/32
!      | 192.168.1.0/24     | 192.168.1.0/24     | 192.168.1.0/24   | 192.168.1.0/24
!      | 192.168.1.0/24     | 192.168.1.0/24     | 192.168.1.0/25   | 192.168.1.0/25
!      | 192.168.1.0/24     | 192.168.1.0/24     | 192.168.1.255/24 | 192.168.1.0/24
!      | 192.168.1.0/24     | 192.168.1.0/24     | 192.168.1.255/25 | 192.168.1.128/25
!      | 10.0.0.0/8         | 10.0.0.0/8         | 10.1.2.3/8       | 10.0.0.0/8
!      | 10.0.0.0/32        | 10.0.0.0/32        | 10.1.2.3/8       | 10.0.0.0/8
!      | 10.1.2.3/32        | 10.1.2.3/32        | 10.1.2.3         | 10.1.2.3/32
!      | 10.1.2.0/24        | 10.1.2.0/24        | 10.1.2.3/24      | 10.1.2.0/24
!      | 10.1.0.0/16        | 10.1.0.0/16        | 10.1.2.3/16      | 10.1.0.0/16
!      | 10.0.0.0/8         | 10.0.0.0/8         | 10.1.2.3/8       | 10.0.0.0/8
!      | 10.0.0.0/8         | 10.0.0.0/8         | 11.1.2.3/8       | 11.0.0.0/8
!      | 10.0.0.0/8         | 10.0.0.0/8         | 9.1.2.3/8        | 9.0.0.0/8
!      | 10:23::f1/128      | 10:23::f1/128      | 10:23::f1/64     | 10:23::/64
!      | 10:23::8000/113    | 10:23::8000/113    | 10:23::ffff      | 10:23::ffff/128
!      | ::ffff:1.2.3.4/128 | ::ffff:1.2.3.4/128 | ::4.3.2.1/24     | ::/24
! (17 rows)

  SELECT '' AS ten, c AS cidr, masklen(c) AS "masklen(cidr)",
    i AS inet, masklen(i) AS "masklen(inet)" FROM INET_TBL;
!  ten |        cidr        | masklen(cidr) |       inet       | masklen(inet)
! -----+--------------------+---------------+------------------+---------------
!      | 192.168.1.0/24     |            24 | 192.168.1.226/24 |            24
!      | 192.168.1.0/24     |            24 | 192.168.1.226    |            32
!      | 192.168.1.0/24     |            24 | 192.168.1.0/24   |            24
!      | 192.168.1.0/24     |            24 | 192.168.1.0/25   |            25
!      | 192.168.1.0/24     |            24 | 192.168.1.255/24 |            24
!      | 192.168.1.0/24     |            24 | 192.168.1.255/25 |            25
!      | 10.0.0.0/8         |             8 | 10.1.2.3/8       |             8
!      | 10.0.0.0/32        |            32 | 10.1.2.3/8       |             8
!      | 10.1.2.3/32        |            32 | 10.1.2.3         |            32
!      | 10.1.2.0/24        |            24 | 10.1.2.3/24      |            24
!      | 10.1.0.0/16        |            16 | 10.1.2.3/16      |            16
!      | 10.0.0.0/8         |             8 | 10.1.2.3/8       |             8
!      | 10.0.0.0/8         |             8 | 11.1.2.3/8       |             8
!      | 10.0.0.0/8         |             8 | 9.1.2.3/8        |             8
!      | 10:23::f1/128      |           128 | 10:23::f1/64     |            64
!      | 10:23::8000/113    |           113 | 10:23::ffff      |           128
!      | ::ffff:1.2.3.4/128 |           128 | ::4.3.2.1/24     |            24
! (17 rows)

  SELECT '' AS four, c AS cidr, masklen(c) AS "masklen(cidr)",
    i AS inet, masklen(i) AS "masklen(inet)" FROM INET_TBL
***************
*** 149,171 ****
    i << c AS sb, i <<= c AS sbe,
    i >> c AS sup, i >>= c AS spe
    FROM INET_TBL;
!  ten |        i         |       c        | lt | le | eq | ge | gt | ne | sb | sbe | sup | spe
! -----+------------------+----------------+----+----+----+----+----+----+----+-----+-----+-----
!      | 192.168.1.226/24 | 192.168.1.0/24 | f  | f  | f  | t  | t  | t  | f  | t   | f   | t
!      | 192.168.1.226    | 192.168.1.0/24 | f  | f  | f  | t  | t  | t  | t  | t   | f   | f
!      | 192.168.1.0/24   | 192.168.1.0/24 | f  | t  | t  | t  | f  | f  | f  | t   | f   | t
!      | 192.168.1.0/25   | 192.168.1.0/24 | f  | f  | f  | t  | t  | t  | t  | t   | f   | f
!      | 192.168.1.255/24 | 192.168.1.0/24 | f  | f  | f  | t  | t  | t  | f  | t   | f   | t
!      | 192.168.1.255/25 | 192.168.1.0/24 | f  | f  | f  | t  | t  | t  | t  | t   | f   | f
!      | 10.1.2.3/8       | 10.0.0.0/8     | f  | f  | f  | t  | t  | t  | f  | t   | f   | t
!      | 10.1.2.3/8       | 10.0.0.0/32    | t  | t  | f  | f  | f  | t  | f  | f   | t   | t
!      | 10.1.2.3         | 10.1.2.3/32    | f  | t  | t  | t  | f  | f  | f  | t   | f   | t
!      | 10.1.2.3/24      | 10.1.2.0/24    | f  | f  | f  | t  | t  | t  | f  | t   | f   | t
!      | 10.1.2.3/16      | 10.1.0.0/16    | f  | f  | f  | t  | t  | t  | f  | t   | f   | t
!      | 10.1.2.3/8       | 10.0.0.0/8     | f  | f  | f  | t  | t  | t  | f  | t   | f   | t
!      | 11.1.2.3/8       | 10.0.0.0/8     | f  | f  | f  | t  | t  | t  | f  | f   | f   | f
!      | 9.1.2.3/8        | 10.0.0.0/8     | t  | t  | f  | f  | f  | t  | f  | f   | f   | f
! (14 rows)

  -- check the conversion to/from text and set_netmask
  SELECT '' AS ten, set_masklen(inet(text(i)), 24) FROM INET_TBL;
--- 171,196 ----
    i << c AS sb, i <<= c AS sbe,
    i >> c AS sup, i >>= c AS spe
    FROM INET_TBL;
!  ten |        i         |       c            | lt | le | eq | ge | gt | ne | sb | sbe | sup | spe
! -----+------------------+--------------------+----+----+----+----+----+----+----+-----+-----+-----
!      | 192.168.1.226/24 | 192.168.1.0/24     | f  | f  | f  | t  | t  | t  | f  | t   | f   | t
!      | 192.168.1.226    | 192.168.1.0/24     | f  | f  | f  | t  | t  | t  | t  | t   | f   | f
!      | 192.168.1.0/24   | 192.168.1.0/24     | f  | t  | t  | t  | f  | f  | f  | t   | f   | t
!      | 192.168.1.0/25   | 192.168.1.0/24     | f  | f  | f  | t  | t  | t  | t  | t   | f   | f
!      | 192.168.1.255/24 | 192.168.1.0/24     | f  | f  | f  | t  | t  | t  | f  | t   | f   | t
!      | 192.168.1.255/25 | 192.168.1.0/24     | f  | f  | f  | t  | t  | t  | t  | t   | f   | f
!      | 10.1.2.3/8       | 10.0.0.0/8         | f  | f  | f  | t  | t  | t  | f  | t   | f   | t
!      | 10.1.2.3/8       | 10.0.0.0/32        | t  | t  | f  | f  | f  | t  | f  | f   | t   | t
!      | 10.1.2.3         | 10.1.2.3/32        | f  | t  | t  | t  | f  | f  | f  | t   | f   | t
!      | 10.1.2.3/24      | 10.1.2.0/24        | f  | f  | f  | t  | t  | t  | f  | t   | f   | t
!      | 10.1.2.3/16      | 10.1.0.0/16        | f  | f  | f  | t  | t  | t  | f  | t   | f   | t
!      | 10.1.2.3/8       | 10.0.0.0/8         | f  | f  | f  | t  | t  | t  | f  | t   | f   | t
!      | 11.1.2.3/8       | 10.0.0.0/8         | f  | f  | f  | t  | t  | t  | f  | f   | f   | f
!      | 9.1.2.3/8        | 10.0.0.0/8         | t  | t  | f  | f  | f  | t  | f  | f   | f   | f
!      | 10:23::f1/64     | 10:23::f1/128      | t  | t  | f  | f  | f  | t  | f  | f   | t   | t
!      | 10:23::ffff      | 10:23::8000/113    | f  | f  | f  | t  | t  | t  | t  | t   | f   | f
!      | ::4.3.2.1/24     | ::ffff:1.2.3.4/128 | t  | t  | f  | f  | f  | t  | f  | f   | t   | t
! (17 rows)

  -- check the conversion to/from text and set_netmask
  SELECT '' AS ten, set_masklen(inet(text(i)), 24) FROM INET_TBL;
***************
*** 185,191 ****
       | 10.1.2.3/24
       | 11.1.2.3/24
       | 9.1.2.3/24
! (14 rows)

  -- check that index works correctly
  CREATE INDEX inet_idx1 ON inet_tbl(i);
--- 210,219 ----
       | 10.1.2.3/24
       | 11.1.2.3/24
       | 9.1.2.3/24
!      | 10:23::f1/24
!      | 10:23::ffff/24
!      | ::4.3.2.1/24
! (17 rows)

  -- check that index works correctly
  CREATE INDEX inet_idx1 ON inet_tbl(i);
Index: src/test/regress/sql/inet.sql
===================================================================
RCS file: /cvsroot/pgsql-server/src/test/regress/sql/inet.sql,v
retrieving revision 1.9
diff -c -c -r1.9 inet.sql
*** src/test/regress/sql/inet.sql    15 Jan 2003 20:01:01 -0000    1.9
--- src/test/regress/sql/inet.sql    12 Jun 2003 05:13:26 -0000
***************
*** 20,35 ****
  INSERT INTO INET_TBL (c, i) VALUES ('10', '10.1.2.3/8');
  INSERT INTO INET_TBL (c, i) VALUES ('10', '11.1.2.3/8');
  INSERT INTO INET_TBL (c, i) VALUES ('10', '9.1.2.3/8');
  -- check that CIDR rejects invalid input:
  INSERT INTO INET_TBL (c, i) VALUES ('192.168.1.2/24', '192.168.1.226');
  -- check that CIDR rejects invalid input when converting from text:
  INSERT INTO INET_TBL (c, i) VALUES (cidr('192.168.1.2/24'), '192.168.1.226');
!
  SELECT '' AS ten, c AS cidr, i AS inet FROM INET_TBL;

  -- now test some support functions

! SELECT '' AS ten, i AS inet, host(i), text(i) FROM INET_TBL;
  SELECT '' AS ten, c AS cidr, broadcast(c),
    i AS inet, broadcast(i) FROM INET_TBL;
  SELECT '' AS ten, c AS cidr, network(c) AS "network(cidr)",
--- 20,39 ----
  INSERT INTO INET_TBL (c, i) VALUES ('10', '10.1.2.3/8');
  INSERT INTO INET_TBL (c, i) VALUES ('10', '11.1.2.3/8');
  INSERT INTO INET_TBL (c, i) VALUES ('10', '9.1.2.3/8');
+ INSERT INTO INET_TBL (c, i) VALUES ('10:23::f1', '10:23::f1/64');
+ INSERT INTO INET_TBL (c, i) VALUES ('10:23::8000/113', '10:23::ffff');
+ INSERT INTO INET_TBL (c, i) VALUES ('::ffff:1.2.3.4', '::4.3.2.1/24');
  -- check that CIDR rejects invalid input:
  INSERT INTO INET_TBL (c, i) VALUES ('192.168.1.2/24', '192.168.1.226');
+ INSERT INTO INET_TBL (c, i) VALUES ('1234::1234::1234', '::1.2.3.4');
  -- check that CIDR rejects invalid input when converting from text:
  INSERT INTO INET_TBL (c, i) VALUES (cidr('192.168.1.2/24'), '192.168.1.226');
! INSERT INTO INET_TBL (c, i) VALUES (cidr('ffff:ffff:ffff:ffff::/24'), '::192.168.1.226');
  SELECT '' AS ten, c AS cidr, i AS inet FROM INET_TBL;

  -- now test some support functions

! SELECT '' AS ten, i AS inet, host(i), text(i), family(i) FROM INET_TBL;
  SELECT '' AS ten, c AS cidr, broadcast(c),
    i AS inet, broadcast(i) FROM INET_TBL;
  SELECT '' AS ten, c AS cidr, network(c) AS "network(cidr)",
Index: network.c
===================================================================
RCS file: /cvsroot/pgsql-server/src/backend/utils/adt/network.c,v
retrieving revision 1.35
retrieving revision 1.41
diff -c -c -r1.35 -r1.41
*** network.c    2 Sep 2002 02:47:04 -0000    1.35
--- network.c    13 May 2003 18:03:07 -0000    1.41
***************
*** 3,9 ****
   *    is for IP V4 CIDR notation, but prepared for V6: just
   *    add the necessary bits where the comments indicate.
   *
!  *    $Header: /cvsroot/pgsql-server/src/backend/utils/adt/network.c,v 1.35 2002/09/02 02:47:04 momjian Exp $
   *
   *    Jon Postel RIP 16 Oct 1998
   */
--- 3,9 ----
   *    is for IP V4 CIDR notation, but prepared for V6: just
   *    add the necessary bits where the comments indicate.
   *
!  *    $Header: /cvsroot/pgsql-server/src/backend/utils/adt/network.c,v 1.41 2003/05/13 18:03:07 tgl Exp $
   *
   *    Jon Postel RIP 16 Oct 1998
   */
***************
*** 16,21 ****
--- 16,22 ----
  #include <arpa/inet.h>

  #include "catalog/pg_type.h"
+ #include "libpq/pqformat.h"
  #include "utils/builtins.h"
  #include "utils/inet.h"

***************
*** 51,59 ****
      int            bits;
      inet       *dst;

-     dst = (inet *) palloc(VARHDRSZ + sizeof(inet_struct));
      /* make sure any unused bits in a CIDR value are zeroed */
!     MemSet(dst, 0, VARHDRSZ + sizeof(inet_struct));

      /* First, try for an IP V4 address: */
      ip_family(dst) = AF_INET;
--- 52,59 ----
      int            bits;
      inet       *dst;

      /* make sure any unused bits in a CIDR value are zeroed */
!     dst = (inet *) palloc0(VARHDRSZ + sizeof(inet_struct));

      /* First, try for an IP V4 address: */
      ip_family(dst) = AF_INET;
***************
*** 149,154 ****
--- 149,249 ----
  }


+ /*
+  *        inet_recv            - converts external binary format to inet
+  *
+  * The external representation is (one byte apiece for)
+  * family, bits, type, address length, address in network byte order.
+  */
+ Datum
+ inet_recv(PG_FUNCTION_ARGS)
+ {
+     StringInfo    buf = (StringInfo) PG_GETARG_POINTER(0);
+     inet       *addr;
+     char       *addrptr;
+     int            bits;
+     int            nb,
+                 i;
+
+     /* make sure any unused bits in a CIDR value are zeroed */
+     addr = (inet *) palloc0(VARHDRSZ + sizeof(inet_struct));
+
+     ip_family(addr) = pq_getmsgbyte(buf);
+     if (ip_family(addr) != AF_INET)
+         elog(ERROR, "Invalid family in external inet");
+     bits = pq_getmsgbyte(buf);
+     if (bits < 0 || bits > 32)
+         elog(ERROR, "Invalid bits in external inet");
+     ip_bits(addr) = bits;
+     ip_type(addr) = pq_getmsgbyte(buf);
+     if (ip_type(addr) != 0 && ip_type(addr) != 1)
+         elog(ERROR, "Invalid type in external inet");
+     nb = pq_getmsgbyte(buf);
+     if (nb != ip_addrsize(addr))
+         elog(ERROR, "Invalid length in external inet");
+
+     VARATT_SIZEP(addr) = VARHDRSZ
+         + ((char *) &ip_v4addr(addr) - (char *) VARDATA(addr))
+         + ip_addrsize(addr);
+
+     addrptr = (char *) &ip_v4addr(addr);
+     for (i = 0; i < nb; i++)
+         addrptr[i] = pq_getmsgbyte(buf);
+
+     /*
+      * Error check: CIDR values must not have any bits set beyond the
+      * masklen. XXX this code is not IPV6 ready.
+      */
+     if (ip_type(addr))
+     {
+         if (!v4addressOK(ip_v4addr(addr), bits))
+             elog(ERROR, "invalid external CIDR value: has bits set to right of mask");
+     }
+
+     PG_RETURN_INET_P(addr);
+ }
+
+ /* share code with INET case */
+ Datum
+ cidr_recv(PG_FUNCTION_ARGS)
+ {
+     return inet_recv(fcinfo);
+ }
+
+ /*
+  *        inet_send            - converts inet to binary format
+  */
+ Datum
+ inet_send(PG_FUNCTION_ARGS)
+ {
+     inet       *addr = PG_GETARG_INET_P(0);
+     StringInfoData buf;
+     char       *addrptr;
+     int            nb,
+                 i;
+
+     pq_begintypsend(&buf);
+     pq_sendbyte(&buf, ip_family(addr));
+     pq_sendbyte(&buf, ip_bits(addr));
+     pq_sendbyte(&buf, ip_type(addr));
+     nb = ip_addrsize(addr);
+     if (nb < 0)
+         nb = 0;
+     pq_sendbyte(&buf, nb);
+     addrptr = (char *) &ip_v4addr(addr);
+     for (i = 0; i < nb; i++)
+         pq_sendbyte(&buf, addrptr[i]);
+     PG_RETURN_BYTEA_P(pq_endtypsend(&buf));
+ }
+
+ /* share code with INET case */
+ Datum
+ cidr_send(PG_FUNCTION_ARGS)
+ {
+     return inet_send(fcinfo);
+ }
+
+
  static Datum
  text_network(text *src, int type)
  {
***************
*** 494,502 ****
      inet       *ip = PG_GETARG_INET_P(0);
      inet       *dst;

-     dst = (inet *) palloc(VARHDRSZ + sizeof(inet_struct));
      /* make sure any unused bits are zeroed */
!     MemSet(dst, 0, VARHDRSZ + sizeof(inet_struct));

      if (ip_family(ip) == AF_INET)
      {
--- 589,596 ----
      inet       *ip = PG_GETARG_INET_P(0);
      inet       *dst;

      /* make sure any unused bits are zeroed */
!     dst = (inet *) palloc0(VARHDRSZ + sizeof(inet_struct));

      if (ip_family(ip) == AF_INET)
      {
***************
*** 534,542 ****
      inet       *ip = PG_GETARG_INET_P(0);
      inet       *dst;

-     dst = (inet *) palloc(VARHDRSZ + sizeof(inet_struct));
      /* make sure any unused bits are zeroed */
!     MemSet(dst, 0, VARHDRSZ + sizeof(inet_struct));

      if (ip_family(ip) == AF_INET)
      {
--- 628,635 ----
      inet       *ip = PG_GETARG_INET_P(0);
      inet       *dst;

      /* make sure any unused bits are zeroed */
!     dst = (inet *) palloc0(VARHDRSZ + sizeof(inet_struct));

      if (ip_family(ip) == AF_INET)
      {
***************
*** 574,582 ****
      inet       *ip = PG_GETARG_INET_P(0);
      inet       *dst;

-     dst = (inet *) palloc(VARHDRSZ + sizeof(inet_struct));
      /* make sure any unused bits are zeroed */
!     MemSet(dst, 0, VARHDRSZ + sizeof(inet_struct));

      if (ip_family(ip) == AF_INET)
      {
--- 667,674 ----
      inet       *ip = PG_GETARG_INET_P(0);
      inet       *dst;

      /* make sure any unused bits are zeroed */
!     dst = (inet *) palloc0(VARHDRSZ + sizeof(inet_struct));

      if (ip_family(ip) == AF_INET)
      {
***************
*** 609,614 ****
--- 701,745 ----
      PG_RETURN_INET_P(dst);
  }

+ Datum
+ network_hostmask(PG_FUNCTION_ARGS)
+ {
+     inet       *ip = PG_GETARG_INET_P(0);
+     inet       *dst;
+
+     /* make sure any unused bits are zeroed */
+     dst = (inet *) palloc0(VARHDRSZ + sizeof(inet_struct));
+
+     if (ip_family(ip) == AF_INET)
+     {
+         /* It's an IP V4 address: */
+         unsigned long mask = 0xffffffff;
+
+         /*
+          * Only shift if the mask len is < 32 bits ..
+          */
+
+         if (ip_bits(ip) < 32)
+             mask >>= ip_bits(ip);
+         else
+             mask = 0;
+
+         ip_v4addr(dst) = htonl(mask);
+
+         ip_bits(dst) = 32;
+     }
+     else
+         /* Go for an IPV6 address here, before faulting out: */
+         elog(ERROR, "unknown address family (%d)", ip_family(ip));
+
+     ip_family(dst) = ip_family(ip);
+     ip_type(dst) = 0;
+     VARATT_SIZEP(dst) = VARHDRSZ
+         + ((char *) &ip_v4addr(dst) - (char *) VARDATA(dst))
+         + ip_addrsize(dst);
+
+     PG_RETURN_INET_P(dst);
+ }

  /*
   * Convert a value of a network datatype to an approximate scalar value.

Re: ipv6 patch #3

From
Bruce Momjian
Date:
I have applied the newest version of this patch.  I did find one warning
reported by my compiler:

    inet_net_ntop.c:273: warning: too many arguments for format
    inet_net_ntop.c:275: warning: too many arguments for format

I have applied the attached patch to prevent the warnings.  Please check
that the patch matches your intentions.

Thanks. I have been waiting for this patch for a long time.

FYI, I also changed the 631 oid because that was already used, probably
by a recent patch.

---------------------------------------------------------------------------

Michael Graff wrote:
> This includes several revisions from others.
>
> Major changes:
>
>         Add ipv6 address parsing support to 'inet' and 'cidr' data types.
>
>         Regression tests for IPv6 operations added.
>
>         Documentation updated to document IPv6 bits.
>
>         Stop treating IPv4 as an "unsigned int" and IPv6 as an array of
>         characters.  Instead, always use the array of characters so we
>         can have one function fits all.  This makes bitncmp(), addressOK(),
>         and several other functions "just work" on both address families.
>
>         add family() function which returns integer 4 or 6 for IPv4 or
>         IPv6.  (See examples below)  Note that to add this new function
>         you will need to dump/initdb/reload or find the correct magic
>         to add the function to the postgresql function catalogs.
>
>         IPv4 addresses always sort before IPv6.
>
>         On disk we use AF_INET for IPv4, and AF_INET+1 for IPv6 addresses.
>         This prevents the need for a dump and reload, but lets IPv6 parsing
>         work on machines without AF_INET6.
>
>         To select all IPv4 addresses from a table:
>
>                 select * from foo where family(addr) = 4 ...
>
>         Order by and other bits should all work.
>
> Regression tests pass, at least on my NetBSD machine.
>

--
  Bruce Momjian                        |  http://candle.pha.pa.us
  pgman@candle.pha.pa.us               |  (610) 359-1001
  +  If your life is a hard drive,     |  13 Roberts Road
  +  Christ can be your backup.        |  Newtown Square, Pennsylvania 19073
Index: src/backend/utils/adt/inet_net_ntop.c
===================================================================
RCS file: /cvsroot/pgsql-server/src/backend/utils/adt/inet_net_ntop.c,v
retrieving revision 1.13
diff -c -c -r1.13 inet_net_ntop.c
*** src/backend/utils/adt/inet_net_ntop.c    24 Jun 2003 22:21:22 -0000    1.13
--- src/backend/utils/adt/inet_net_ntop.c    24 Jun 2003 22:22:44 -0000
***************
*** 270,278 ****

      if (!double_colon) {
          if (bits < 128 - 32)
!             cp += SPRINTF((cp, "::", bits));
          else if (bits < 128 - 16)
!             cp += SPRINTF((cp, ":0", bits));
      }

      /* Format CIDR /width. */
--- 270,278 ----

      if (!double_colon) {
          if (bits < 128 - 32)
!             cp += SPRINTF((cp, "::%d", bits));
          else if (bits < 128 - 16)
!             cp += SPRINTF((cp, ":0%d", bits));
      }

      /* Format CIDR /width. */

Re: ipv6 patch #3

From
Michael Graff
Date:
Ok, I think the ,bits part was in error.  Go ahead and remove the %d
and the ,bits part.

--Michael

Bruce Momjian <pgman@candle.pha.pa.us> writes:

> Index: src/backend/utils/adt/inet_net_ntop.c
> ===================================================================
> RCS file: /cvsroot/pgsql-server/src/backend/utils/adt/inet_net_ntop.c,v
> retrieving revision 1.13
> diff -c -c -r1.13 inet_net_ntop.c
> *** src/backend/utils/adt/inet_net_ntop.c    24 Jun 2003 22:21:22 -0000    1.13
> --- src/backend/utils/adt/inet_net_ntop.c    24 Jun 2003 22:22:44 -0000
> ***************
> *** 270,278 ****
>
>       if (!double_colon) {
>           if (bits < 128 - 32)
> !             cp += SPRINTF((cp, "::", bits));
>           else if (bits < 128 - 16)
> !             cp += SPRINTF((cp, ":0", bits));
>       }
>
>       /* Format CIDR /width. */
> --- 270,278 ----
>
>       if (!double_colon) {
>           if (bits < 128 - 32)
> !             cp += SPRINTF((cp, "::%d", bits));
>           else if (bits < 128 - 16)
> !             cp += SPRINTF((cp, ":0%d", bits));
>       }
>
>       /* Format CIDR /width. */

Re: ipv6 patch #3

From
Bruce Momjian
Date:
OK, done.

---------------------------------------------------------------------------

Michael Graff wrote:
> Ok, I think the ,bits part was in error.  Go ahead and remove the %d
> and the ,bits part.
>
> --Michael
>
> Bruce Momjian <pgman@candle.pha.pa.us> writes:
>
> > Index: src/backend/utils/adt/inet_net_ntop.c
> > ===================================================================
> > RCS file: /cvsroot/pgsql-server/src/backend/utils/adt/inet_net_ntop.c,v
> > retrieving revision 1.13
> > diff -c -c -r1.13 inet_net_ntop.c
> > *** src/backend/utils/adt/inet_net_ntop.c    24 Jun 2003 22:21:22 -0000    1.13
> > --- src/backend/utils/adt/inet_net_ntop.c    24 Jun 2003 22:22:44 -0000
> > ***************
> > *** 270,278 ****
> >
> >       if (!double_colon) {
> >           if (bits < 128 - 32)
> > !             cp += SPRINTF((cp, "::", bits));
> >           else if (bits < 128 - 16)
> > !             cp += SPRINTF((cp, ":0", bits));
> >       }
> >
> >       /* Format CIDR /width. */
> > --- 270,278 ----
> >
> >       if (!double_colon) {
> >           if (bits < 128 - 32)
> > !             cp += SPRINTF((cp, "::%d", bits));
> >           else if (bits < 128 - 16)
> > !             cp += SPRINTF((cp, ":0%d", bits));
> >       }
> >
> >       /* Format CIDR /width. */
>

--
  Bruce Momjian                        |  http://candle.pha.pa.us
  pgman@candle.pha.pa.us               |  (610) 359-1001
  +  If your life is a hard drive,     |  13 Roberts Road
  +  Christ can be your backup.        |  Newtown Square, Pennsylvania 19073