From: Tom I Helbekkmo <tih@Hamartun.Priv.NO>

PostgreSQL type extensions for IP and MAC addresses.

I needed to record IP and MAC level ethernet addresses in a data
base, and I really didn't want to store them as plain strings, with
no enforced error checking, so I put together the accompanying code
as my first experiment with adding a data type to PostgreSQL.  I
then thought that this might be useful to others, both directly and
as a very simple example of how to do this sort of thing, so here
it is, in the hope that it will be useful.
This commit is contained in:
Marc G. Fournier 1998-01-25 07:11:07 +00:00
parent 9f8d3b668b
commit 674b22a2a4
7 changed files with 834 additions and 0 deletions

View File

@ -0,0 +1,20 @@
# PostgreSQL type definitions for IP and MAC addresses.
all: ip.so mac.so
ip.so: ip.o
ld -Bshareable -o ip.so ip.o
ip.o: ip.c
cc -g -O -fPIC -I/usr/local/pgsql/include -c ip.c
mac.so: mac.o
ld -Bshareable -o mac.so mac.o
mac.o: mac.c mac.h
cc -g -O -fPIC -I/usr/local/pgsql/include -c mac.c
install: ip.so mac.so
install -c ip.so mac.so /usr/local/pgsql/modules
# eof

51
contrib/ip_and_mac/README Normal file
View File

@ -0,0 +1,51 @@
PostgreSQL type extensions for IP and MAC addresses.
---------------------------------------------------
I needed to record IP and MAC level ethernet addresses in a data
base, and I really didn't want to store them as plain strings, with
no enforced error checking, so I put together the accompanying code
as my first experiment with adding a data type to PostgreSQL. I
then thought that this might be useful to others, both directly and
as a very simple example of how to do this sort of thing, so here
it is, in the hope that it will be useful.
IP addresses are implemented as an 8 byte struct (this may well be
more than is useful, but I figured that since it has to be at least 5,
it might as well round well) that contains the four bytes of address
and a mask width. Thus, a node address looks like '158.37.96.15/32'
(or just '158.37.96.15', which is understood to mean the same thing).
This address happens to be part of a subnet where I work;
'158.37.96.0/24', which itself is a part of the larger subnet
allocated to our institution, which is '158.37.96.0/21', which again,
if you go by the book, is part of the class "B" net '158.37.0.0/16'.
Input and output functions are supplied, along with the "normal" <,
<=, =, >=, > and <> operators, which all do what you expect, and the
similarity operator ~~, which checks whether two given addresses are
either the same, or, failing that, whether one is a subnet
specification and the other an address (or a smaller subnet) within
that. Good for picking out records with addresses in a given subnet:
note that '158.37.96.0/21' spans '158.37.96.0' to '158.37.103.255',
which is not all that easily handled in its external representation.
MAC level ethernet addresses are also implemented as an 8 byte struct
(I wish I knew what alignment needs are actually present -- I'm just
not taking any chances here) that contains the address as unsigned
chars. Several input forms are accepted: the following are all the
same address: '08002b:010203', '08002b-010203', '0800.2b01.0203',
'08-00-2b-01-02-03' and '08:00:2b:01:02:03'. Upper and lower case is
accepted for the digits 'a' through 'f'. Output is always in the
latter of the given forms.
Input and output functions are supplied, along with the = and <>
operators, which do what you expect, and the similarity operator ~~,
which checks whether two given addresses belong to hardware from the
same manufacturer (first three bytes the same, that is). As an extra
feature, a function macaddr_manuf() is defined, which returns the name
of the manufacturer as a string.
To install: fix the path names in the SQL files and the Makefile if
you need to, then make, make install, slurp the SQL files into psql or
whatever, and you're off. Enjoy!
Bergen, Norway, 1998-01-11, Tom Ivar Helbekkmo (tih@Hamartun.Priv.NO).

212
contrib/ip_and_mac/ip.c Normal file
View File

@ -0,0 +1,212 @@
/*
* PostgreSQL type definitions for IP addresses.
*/
#include <stdio.h>
#include <postgres.h>
#include <utils/palloc.h>
/*
* This is the internal storage format for IP addresses:
*/
typedef struct ipaddr {
unsigned char a;
unsigned char b;
unsigned char c;
unsigned char d;
unsigned char w;
unsigned char pad1;
short pad2;
} ipaddr;
/*
* Various forward declarations:
*/
ipaddr *ipaddr_in(char *str);
char *ipaddr_out(ipaddr *addr);
bool ipaddr_lt(ipaddr *a1, ipaddr *a2);
bool ipaddr_le(ipaddr *a1, ipaddr *a2);
bool ipaddr_eq(ipaddr *a1, ipaddr *a2);
bool ipaddr_ge(ipaddr *a1, ipaddr *a2);
bool ipaddr_gt(ipaddr *a1, ipaddr *a2);
bool ipaddr_ne(ipaddr *a1, ipaddr *a2);
int4 ipaddr_cmp(ipaddr *a1, ipaddr *a2);
bool ipaddr_like(ipaddr *a1, ipaddr *a2);
/*
* A utility macro used for sorting addresses numerically:
*/
#define Mag(addr) \
((unsigned long)((addr->a<<24)|(addr->b<<16)|(addr->c<<8)|(addr->d)))
/*
* IP address reader. Note how the count returned by sscanf()
* is used to determine whether the mask size was specified.
*/
ipaddr *ipaddr_in(char *str) {
int a, b, c, d, w;
ipaddr *result;
int count;
if (strlen(str) > 0) {
count = sscanf(str, "%d.%d.%d.%d/%d", &a, &b, &c, &d, &w);
if (count < 4) {
elog(ERROR, "ipaddr_in: error in parsing \"%s\"", str);
return(NULL);
}
if (count == 4)
w = 32;
if ((a < 0) || (a > 255) || (b < 0) || (b > 255) ||
(c < 0) || (c > 255) || (d < 0) || (d > 255) ||
(w < 0) || (w > 32)) {
elog(ERROR, "ipaddr_in: illegal address \"%s\"", str);
return(NULL);
}
} else {
a = b = c = d = w = 0; /* special case for missing address */
}
result = (ipaddr *)palloc(sizeof(ipaddr));
result->a = a;
result->b = b;
result->c = c;
result->d = d;
result->w = w;
return(result);
}
/*
* IP address output function. Note mask size specification
* generated only for subnets, not for plain host addresses.
*/
char *ipaddr_out(ipaddr *addr) {
char *result;
if (addr == NULL)
return(NULL);
result = (char *)palloc(32);
if (Mag(addr) > 0) {
if (addr->w == 32)
sprintf(result, "%d.%d.%d.%d",
addr->a, addr->b, addr->c, addr->d);
else
sprintf(result, "%d.%d.%d.%d/%d",
addr->a, addr->b, addr->c, addr->d, addr->w);
} else {
result[0] = 0; /* special case for missing address */
}
return(result);
}
/*
* Boolean tests. The Mag() macro was defined above.
*/
bool ipaddr_lt(ipaddr *a1, ipaddr *a2) {
unsigned long a1mag, a2mag;
a1mag = Mag(a1);
a2mag = Mag(a2);
return (a1mag < a2mag);
};
bool ipaddr_le(ipaddr *a1, ipaddr *a2) {
unsigned long a1mag, a2mag;
a1mag = Mag(a1);
a2mag = Mag(a2);
return (a1mag <= a2mag);
};
bool ipaddr_eq(ipaddr *a1, ipaddr *a2) {
unsigned long a1mag, a2mag;
a1mag = Mag(a1);
a2mag = Mag(a2);
return ((a1mag == a2mag) && (a1->w == a2->w));
};
bool ipaddr_ge(ipaddr *a1, ipaddr *a2) {
unsigned long a1mag, a2mag;
a1mag = Mag(a1);
a2mag = Mag(a2);
return (a1mag >= a2mag);
};
bool ipaddr_gt(ipaddr *a1, ipaddr *a2) {
unsigned long a1mag, a2mag;
a1mag = Mag(a1);
a2mag = Mag(a2);
return (a1mag > a2mag);
};
bool ipaddr_ne(ipaddr *a1, ipaddr *a2) {
unsigned long a1mag, a2mag;
a1mag = Mag(a1);
a2mag = Mag(a2);
return ((a1mag != a2mag) || (a1->w != a2->w));
};
/*
* Comparison function for sorting:
*/
int4 ipaddr_cmp(ipaddr *a1, ipaddr *a2) {
unsigned long a1mag = Mag(a1), a2mag = Mag(a2);
if (a1mag < a2mag)
return -1;
else if (a1mag > a2mag)
return 1;
else
return 0;
}
/*
* Our "similarity" operator checks whether two addresses are
* either the same node address, or, failing that, whether one
* of them contains the other. This will be true if they have
* the same high bits down as far as the shortest mask reaches.
*/
unsigned long build_mask(unsigned char bits) {
unsigned long mask = 0;
int i;
for (i = 0; i < bits; i++)
mask = (mask >> 1) | 0x80000000;
return mask;
}
bool ipaddr_like(ipaddr *a1, ipaddr *a2) {
unsigned long a1bits, a2bits, maskbits;
if ((a1->w == 0) || (a2->w == 0))
return FALSE;
if ((a1->w == 32) && (a2->w == 32))
return ipaddr_eq(a1, a2);
a1bits = Mag(a1);
a2bits = Mag(a2);
if (a1->w > a2->w) {
maskbits = build_mask(a2->w);
return ((a1bits & maskbits) == (a2bits & maskbits));
} else {
maskbits = build_mask(a1->w);
return ((a2bits & maskbits) == (a1bits & maskbits));
}
return FALSE;
}
/*
* eof
*/

131
contrib/ip_and_mac/ip.sql Normal file
View File

@ -0,0 +1,131 @@
--
-- PostgreSQL code for IP addresses.
--
load '/usr/local/pgsql/modules/ip.so';
--
-- Input and output functions and the type itself:
--
create function ipaddr_in(opaque)
returns opaque
as '/usr/local/pgsql/modules/ip.so'
language 'c';
create function ipaddr_out(opaque)
returns opaque
as '/usr/local/pgsql/modules/ip.so'
language 'c';
create type ipaddr (
internallength = 8,
externallength = variable,
input = ipaddr_in,
output = ipaddr_out
);
--
-- The various boolean tests:
--
create function ipaddr_lt(ipaddr, ipaddr)
returns bool
as '/usr/local/pgsql/modules/ip.so'
language 'c';
create function ipaddr_le(ipaddr, ipaddr)
returns bool
as '/usr/local/pgsql/modules/ip.so'
language 'c';
create function ipaddr_eq(ipaddr, ipaddr)
returns bool
as '/usr/local/pgsql/modules/ip.so'
language 'c';
create function ipaddr_ge(ipaddr, ipaddr)
returns bool
as '/usr/local/pgsql/modules/ip.so'
language 'c';
create function ipaddr_gt(ipaddr, ipaddr)
returns bool
as '/usr/local/pgsql/modules/ip.so'
language 'c';
create function ipaddr_ne(ipaddr, ipaddr)
returns bool
as '/usr/local/pgsql/modules/ip.so'
language 'c';
create function ipaddr_like(ipaddr, ipaddr)
returns bool
as '/usr/local/pgsql/modules/ip.so'
language 'c';
--
-- Now the operators. Note how some of the parameters to some
-- of the 'create operator' commands are commented out. This
-- is because they reference as yet undefined operators, and
-- will be implicitly defined when those are, further down.
--
create operator <= (
leftarg = ipaddr,
rightarg = ipaddr,
-- commutator = >,
-- negator = >,
procedure = ipaddr_le
);
create operator < (
leftarg = ipaddr,
rightarg = ipaddr,
-- commutator = >=,
-- negator = >=,
procedure = ipaddr_lt
);
create operator = (
leftarg = ipaddr,
rightarg = ipaddr,
commutator = =,
-- negator = <>,
procedure = ipaddr_eq
);
create operator >= (
leftarg = ipaddr,
rightarg = ipaddr,
commutator = <,
negator = <,
procedure = ipaddr_ge
);
create operator > (
leftarg = ipaddr,
rightarg = ipaddr,
commutator = <=,
negator = <=,
procedure = ipaddr_gt
);
create operator <> (
leftarg = ipaddr,
rightarg = ipaddr,
commutator = <>,
negator = =,
procedure = ipaddr_ne
);
create operator ~~ (
leftarg = ipaddr,
rightarg = ipaddr,
commutator = ~~,
procedure = ipaddr_like
);
--
-- eof
--

202
contrib/ip_and_mac/mac.c Normal file
View File

@ -0,0 +1,202 @@
/*
* PostgreSQL type definitions for MAC addresses.
*/
#include <stdio.h>
#include <postgres.h>
#include <utils/palloc.h>
#include "mac.h"
/*
* This is the internal storage format for MAC addresses:
*/
typedef struct macaddr {
unsigned char a;
unsigned char b;
unsigned char c;
unsigned char d;
unsigned char e;
unsigned char f;
short pad;
} macaddr;
/*
* Various forward declarations:
*/
macaddr *macaddr_in(char *str);
char *macaddr_out(macaddr *addr);
bool macaddr_eq(macaddr *a1, macaddr *a2);
bool macaddr_ne(macaddr *a1, macaddr *a2);
int4 macaddr_cmp(macaddr *a1, macaddr *a2);
bool macaddr_like(macaddr *a1, macaddr *a2);
text *macaddr_manuf(macaddr *addr);
/*
* Utility macros used for sorting and comparing:
*/
#define MagM(addr) \
((unsigned long)((addr->a<<16)|(addr->b<<8)|(addr->c)))
#define MagH(addr) \
((unsigned long)((addr->c<<16)|(addr->e<<8)|(addr->f)))
/*
* MAC address reader. Accepts several common notations.
*/
macaddr *macaddr_in(char *str) {
int a, b, c, d, e, f;
macaddr *result;
int count;
if (strlen(str) > 0) {
count = sscanf(str, "%x:%x:%x:%x:%x:%x", &a, &b, &c, &d, &e, &f);
if (count != 6)
count = sscanf(str, "%x-%x-%x-%x-%x-%x", &a, &b, &c, &d, &e, &f);
if (count != 6)
count = sscanf(str, "%2x%2x%2x:%2x%2x%2x", &a, &b, &c, &d, &e, &f);
if (count != 6)
count = sscanf(str, "%2x%2x%2x-%2x%2x%2x", &a, &b, &c, &d, &e, &f);
if (count != 6)
count = sscanf(str, "%2x%2x.%2x%2x.%2x%2x", &a, &b, &c, &d, &e, &f);
if (count != 6) {
elog(ERROR, "macaddr_in: error in parsing \"%s\"", str);
return(NULL);
}
if ((a < 0) || (a > 255) || (b < 0) || (b > 255) ||
(c < 0) || (c > 255) || (d < 0) || (d > 255) ||
(e < 0) || (e > 255) || (f < 0) || (f > 255)) {
elog(ERROR, "macaddr_in: illegal address \"%s\"", str);
return(NULL);
}
} else {
a = b = c = d = e = f = 0; /* special case for missing address */
}
result = (macaddr *)palloc(sizeof(macaddr));
result->a = a;
result->b = b;
result->c = c;
result->d = d;
result->e = e;
result->f = f;
return(result);
}
/*
* MAC address output function. Fixed format.
*/
char *macaddr_out(macaddr *addr) {
char *result;
if (addr == NULL)
return(NULL);
result = (char *)palloc(32);
if ((MagM(addr) > 0) || (MagH(addr) > 0)) {
sprintf(result, "%02x:%02x:%02x:%02x:%02x:%02x",
addr->a, addr->b, addr->c, addr->d, addr->e, addr->f);
} else {
result[0] = 0; /* special case for missing address */
}
return(result);
}
/*
* Boolean tests.
*/
bool macaddr_eq(macaddr *a1, macaddr *a2) {
return((a1->a == a2->a) && (a1->b == a2->b) &&
(a1->c == a2->c) && (a1->d == a2->d) &&
(a1->e == a2->e) && (a1->f == a2->f));
};
bool macaddr_ne(macaddr *a1, macaddr *a2) {
return((a1->a != a2->a) || (a1->b != a2->b) ||
(a1->c != a2->c) || (a1->d != a2->d) ||
(a1->e != a2->e) || (a1->f != a2->f));
};
/*
* Comparison function for sorting:
*/
int4 macaddr_cmp(macaddr *a1, macaddr *a2) {
unsigned long a1magm, a1magh, a2magm, a2magh;
a1magm = MagM(a1);
a1magh = MagH(a1);
a2magm = MagM(a2);
a2magh = MagH(a2);
if (a1magm < a2magm)
return -1;
else if (a1magm > a2magm)
return 1;
else if (a1magh < a2magh)
return -1;
else if (a1magh > a2magh)
return 1;
else
return 0;
}
/*
* Similarity means having the same manufacurer, which means
* having the same first three bytes of address:
*/
bool macaddr_like(macaddr *a1, macaddr *a2) {
unsigned long a1magm, a2magm;
a1magm = MagM(a1);
a2magm = MagM(a2);
if ((a1magm == 0) || (a2magm == 0))
return FALSE;
return (a1magm == a2magm);
}
/*
* The special manufacturer fetching function. See "mac.h".
*/
text *macaddr_manuf(macaddr *addr) {
manufacturer *manuf;
int length;
text *result;
for (manuf = manufacturers; manuf->name != NULL; manuf++) {
if ((manuf->a == addr->a) &&
(manuf->b == addr->b) &&
(manuf->c == addr->c))
break;
}
if (manuf->name == NULL) {
result = palloc(VARHDRSZ + 1);
memset(result, 0, VARHDRSZ + 1);
VARSIZE(result) = VARHDRSZ + 1;
} else {
length = strlen(manuf->name) + 1;
result = palloc(length + VARHDRSZ);
memset(result, 0, length + VARHDRSZ);
VARSIZE(result) = length + VARHDRSZ;
memcpy(VARDATA(result), manuf->name, length);
}
return result;
}
/*
* eof
*/

130
contrib/ip_and_mac/mac.h Normal file
View File

@ -0,0 +1,130 @@
/*
* PostgreSQL type definitions for MAC addresses.
*/
typedef struct manufacturer {
unsigned char a;
unsigned char b;
unsigned char c;
char *name;
} manufacturer;
manufacturer manufacturers[] = {
{0x00, 0x00, 0x0C, "Cisco"},
{0x00, 0x00, 0x0E, "Fujitsu"},
{0x00, 0x00, 0x0F, "NeXT"},
{0x00, 0x00, 0x10, "Sytek"},
{0x00, 0x00, 0x1D, "Cabletron"},
{0x00, 0x00, 0x20, "DIAB"},
{0x00, 0x00, 0x22, "Visual Technology"},
{0x00, 0x00, 0x2A, "TRW"},
{0x00, 0x00, 0x32, "GPT Limited"},
{0x00, 0x00, 0x5A, "S & Koch"},
{0x00, 0x00, 0x5E, "IANA"},
{0x00, 0x00, 0x65, "Network General"},
{0x00, 0x00, 0x6B, "MIPS"},
{0x00, 0x00, 0x77, "MIPS"},
{0x00, 0x00, 0x7A, "Ardent"},
{0x00, 0x00, 0x89, "Cayman Systems"},
{0x00, 0x00, 0x93, "Proteon"},
{0x00, 0x00, 0x9F, "Ameristar Technology"},
{0x00, 0x00, 0xA2, "Wellfleet"},
{0x00, 0x00, 0xA3, "Network Application Technology"},
{0x00, 0x00, 0xA6, "Network General"},
{0x00, 0x00, 0xA7, "NCD"},
{0x00, 0x00, 0xA9, "Network Systems"},
{0x00, 0x00, 0xAA, "Xerox"},
{0x00, 0x00, 0xB3, "CIMLinc"},
{0x00, 0x00, 0xB7, "Dove Fastnet"},
{0x00, 0x00, 0xBC, "Allen-Bradley"},
{0x00, 0x00, 0xC0, "Western Digital"},
{0x00, 0x00, 0xC5, "Farallon"},
{0x00, 0x00, 0xC6, "Hewlett-Packard"},
{0x00, 0x00, 0xC8, "Altos"},
{0x00, 0x00, 0xC9, "Emulex"},
{0x00, 0x00, 0xD7, "Dartmouth College"},
{0x00, 0x00, 0xD8, "3Com (?)"},
{0x00, 0x00, 0xDD, "Gould"},
{0x00, 0x00, 0xDE, "Unigraph"},
{0x00, 0x00, 0xE2, "Acer Counterpoint"},
{0x00, 0x00, 0xEF, "Alantec"},
{0x00, 0x00, 0xFD, "High Level Hardware"},
{0x00, 0x01, 0x02, "BBN internal usage"},
{0x00, 0x20, 0xAF, "3Com"},
{0x00, 0x17, 0x00, "Kabel"},
{0x00, 0x80, 0x64, "Wyse Technology"},
{0x00, 0x80, 0x2B, "IMAC (?)"},
{0x00, 0x80, 0x2D, "Xylogics, Inc."},
{0x00, 0x80, 0x8C, "Frontier Software Development"},
{0x00, 0x80, 0xC2, "IEEE 802.1 Committee"},
{0x00, 0x80, 0xD3, "Shiva"},
{0x00, 0xAA, 0x00, "Intel"},
{0x00, 0xDD, 0x00, "Ungermann-Bass"},
{0x00, 0xDD, 0x01, "Ungermann-Bass"},
{0x02, 0x07, 0x01, "Racal InterLan"},
{0x02, 0x04, 0x06, "BBN internal usage"},
{0x02, 0x60, 0x86, "Satelcom MegaPac"},
{0x02, 0x60, 0x8C, "3Com"},
{0x02, 0xCF, 0x1F, "CMC"},
{0x08, 0x00, 0x02, "3Com"},
{0x08, 0x00, 0x03, "ACC"},
{0x08, 0x00, 0x05, "Symbolics"},
{0x08, 0x00, 0x08, "BBN"},
{0x08, 0x00, 0x09, "Hewlett-Packard"},
{0x08, 0x00, 0x0A, "Nestar Systems"},
{0x08, 0x00, 0x0B, "Unisys"},
{0x08, 0x00, 0x11, "Tektronix"},
{0x08, 0x00, 0x14, "Excelan"},
{0x08, 0x00, 0x17, "NSC"},
{0x08, 0x00, 0x1A, "Data General"},
{0x08, 0x00, 0x1B, "Data General"},
{0x08, 0x00, 0x1E, "Apollo"},
{0x08, 0x00, 0x20, "Sun"},
{0x08, 0x00, 0x22, "NBI"},
{0x08, 0x00, 0x25, "CDC"},
{0x08, 0x00, 0x26, "Norsk Data"},
{0x08, 0x00, 0x27, "PCS Computer Systems GmbH"},
{0x08, 0x00, 0x28, "Texas Instruments"},
{0x08, 0x00, 0x2B, "DEC"},
{0x08, 0x00, 0x2E, "Metaphor"},
{0x08, 0x00, 0x2F, "Prime Computer"},
{0x08, 0x00, 0x36, "Intergraph"},
{0x08, 0x00, 0x37, "Fujitsu-Xerox"},
{0x08, 0x00, 0x38, "Bull"},
{0x08, 0x00, 0x39, "Spider Systems"},
{0x08, 0x00, 0x41, "DCA Digital Comm. Assoc."},
{0x08, 0x00, 0x45, "Xylogics (?)"},
{0x08, 0x00, 0x46, "Sony"},
{0x08, 0x00, 0x47, "Sequent"},
{0x08, 0x00, 0x49, "Univation"},
{0x08, 0x00, 0x4C, "Encore"},
{0x08, 0x00, 0x4E, "BICC"},
{0x08, 0x00, 0x56, "Stanford University"},
{0x08, 0x00, 0x58, "DECsystem 20 (?)"},
{0x08, 0x00, 0x5A, "IBM"},
{0x08, 0x00, 0x67, "Comdesign"},
{0x08, 0x00, 0x68, "Ridge"},
{0x08, 0x00, 0x69, "Silicon Graphics"},
{0x08, 0x00, 0x6E, "Concurrent"},
{0x08, 0x00, 0x75, "DDE"},
{0x08, 0x00, 0x7C, "Vitalink"},
{0x08, 0x00, 0x80, "XIOS"},
{0x08, 0x00, 0x86, "Imagen/QMS"},
{0x08, 0x00, 0x87, "Xyplex"},
{0x08, 0x00, 0x89, "Kinetics"},
{0x08, 0x00, 0x8B, "Pyramid"},
{0x08, 0x00, 0x8D, "XyVision"},
{0x08, 0x00, 0x90, "Retix Inc"},
{0x48, 0x44, 0x53, "HDS (?)"},
{0x80, 0x00, 0x10, "AT&T"},
{0xAA, 0x00, 0x00, "DEC"},
{0xAA, 0x00, 0x01, "DEC"},
{0xAA, 0x00, 0x02, "DEC"},
{0xAA, 0x00, 0x03, "DEC"},
{0xAA, 0x00, 0x04, "DEC"},
{0x00, 0x00, 0x00, NULL}
};
/*
* eof
*/

View File

@ -0,0 +1,88 @@
--
-- PostgreSQL code for MAC addresses.
--
load '/usr/local/pgsql/modules/mac.so';
--
-- Input and output functions and the type itself:
--
create function macaddr_in(opaque)
returns opaque
as '/usr/local/pgsql/modules/mac.so'
language 'c';
create function macaddr_out(opaque)
returns opaque
as '/usr/local/pgsql/modules/mac.so'
language 'c';
create type macaddr (
internallength = 8,
externallength = variable,
input = macaddr_in,
output = macaddr_out
);
--
-- The various boolean tests:
--
create function macaddr_eq(macaddr, macaddr)
returns bool
as '/usr/local/pgsql/modules/mac.so'
language 'c';
create function macaddr_ne(macaddr, macaddr)
returns bool
as '/usr/local/pgsql/modules/mac.so'
language 'c';
create function macaddr_like(macaddr, macaddr)
returns bool
as '/usr/local/pgsql/modules/mac.so'
language 'c';
--
-- Now the operators. Note how the "negator = <>" in the
-- definition of the equivalence operator is commented out.
-- It gets defined implicitly when "<>" is defined, with
-- "=" as its negator.
--
create operator = (
leftarg = macaddr,
rightarg = macaddr,
commutator = =,
-- negator = <>,
procedure = macaddr_eq
);
create operator <> (
leftarg = macaddr,
rightarg = macaddr,
commutator = <>,
negator = =,
procedure = macaddr_ne
);
create operator ~~ (
leftarg = macaddr,
rightarg = macaddr,
commutator = ~~,
procedure = macaddr_like
);
--
-- Finally, the special manufacurer matching function:
--
create function macaddr_manuf(macaddr)
returns text
as '/usr/local/pgsql/modules/mac.so'
language 'c';
--
-- eof
--