From 3fa676a74cd7f4e010610e1da1bf17395b1d05c7 Mon Sep 17 00:00:00 2001 From: "Marc G. Fournier" Date: Mon, 17 Aug 1998 03:35:05 +0000 Subject: [PATCH] From: Garrett Wollman Here is some more contrib-fodder, based on TIH's IP address type, for ISBN and ISSN identifiers (which I just happened to need to keep track of the things in my library). --- contrib/README | 6 ++ contrib/isbn_issn/Makefile | 25 +++++ contrib/isbn_issn/README | 18 ++++ contrib/isbn_issn/isbn.c | 187 +++++++++++++++++++++++++++++++++++++ contrib/isbn_issn/isbn.sql | 116 +++++++++++++++++++++++ contrib/isbn_issn/issn.c | 178 +++++++++++++++++++++++++++++++++++ contrib/isbn_issn/issn.sql | 116 +++++++++++++++++++++++ 7 files changed, 646 insertions(+) create mode 100644 contrib/isbn_issn/Makefile create mode 100644 contrib/isbn_issn/README create mode 100644 contrib/isbn_issn/isbn.c create mode 100644 contrib/isbn_issn/isbn.sql create mode 100644 contrib/isbn_issn/issn.c create mode 100644 contrib/isbn_issn/issn.sql diff --git a/contrib/README b/contrib/README index bf4d10ebc56..800c9344a6b 100644 --- a/contrib/README +++ b/contrib/README @@ -35,6 +35,10 @@ ip_and_mac - PostgreSQL type extensions for IP and MAC addresses by Tom Ivar Helbekkmo +isbn_issn - + PostgreSQL type extensions for ISBN (books) and ISSN (serials) + by Garrett A. Wollman + linux - Start postgres back end system by Thomas Lockhart @@ -80,3 +84,5 @@ userlock - User locks by Massimo Dal Zotto + + diff --git a/contrib/isbn_issn/Makefile b/contrib/isbn_issn/Makefile new file mode 100644 index 00000000000..baccaf28e6c --- /dev/null +++ b/contrib/isbn_issn/Makefile @@ -0,0 +1,25 @@ +# +# PostgreSQL types for ISBN and ISSN identifiers. +# +# $Id: Makefile,v 1.1 1998/08/17 03:35:04 scrappy Exp $ + +all: isbn.so issn.so + +isbn.so: isbn.o + ld -Bshareable -o isbn.so isbn.o + +isbn.o: isbn.c + cc -g -O -fPIC -I/usr/local/pgsql/include -c isbn.c + +issn.so: issn.o + ld -Bshareable -o issn.so issn.o + +issn.o: issn.c + cc -g -O -fPIC -I/usr/local/pgsql/include -c issn.c + +install: isbn.so issn.so + install -c isbn.so issn.so /usr/local/pgsql/modules + +# +# eof +# diff --git a/contrib/isbn_issn/README b/contrib/isbn_issn/README new file mode 100644 index 00000000000..6b29eb929b4 --- /dev/null +++ b/contrib/isbn_issn/README @@ -0,0 +1,18 @@ +This directory contains definitions for a couple of PostgreSQL +external types, for a couple of international-standard namespaces: +ISBN (books) and ISSN (serials). Rather than just using a char() +member of the appropriate length, I wanted my database to include +the validity-checking that both these numbering systems were designed +to encompass. A little bit of research revealed the formulae +for computing the check digits, and I also included some validity +constraints on the number of hyphens. + +The internal representation of these types is intended to be +compatible with `char16', in the (perhaps vain) hope that +this will make it possible to create indices of these types +using char16_ops. + +These are based on Tom Ivar Helbekkmo's IP address type definition, +from which I have copied the entire form of the implementation. + +Garrett A. Wollman, August 1998 diff --git a/contrib/isbn_issn/isbn.c b/contrib/isbn_issn/isbn.c new file mode 100644 index 00000000000..ed608f3e133 --- /dev/null +++ b/contrib/isbn_issn/isbn.c @@ -0,0 +1,187 @@ +/* + * PostgreSQL type definitions for ISBNs. + * + * $Id: isbn.c,v 1.1 1998/08/17 03:35:04 scrappy Exp $ + */ + +#include + +#include +#include + +/* + * This is the internal storage format for ISBNs. + * NB: This is an intentional type pun with builtin type `char16'. + */ + +typedef struct isbn +{ + char num[13]; + char pad[3]; +} isbn; + +/* + * Various forward declarations: + */ + +isbn *isbn_in(char *str); +char *isbn_out(isbn * addr); + +bool isbn_lt(isbn * a1, isbn * a2); +bool isbn_le(isbn * a1, isbn * a2); +bool isbn_eq(isbn * a1, isbn * a2); +bool isbn_ge(isbn * a1, isbn * a2); +bool isbn_gt(isbn * a1, isbn * a2); + +bool isbn_ne(isbn * a1, isbn * a2); + +int4 isbn_cmp(isbn * a1, isbn * a2); + +int4 isbn_sum(char *str); + +/* + * ISBN reader. + */ + +isbn * +isbn_in(char *str) +{ + isbn *result; + char *cp; + int count; + + if (strlen(str) != 13) { + elog(ERROR, "isbn_in: invalid ISBN \"%s\"", str); + return (NULL); + } + if (isbn_sum(str) != 0) { + elog(ERROR, "isbn_in: purported ISBN \"%s\" failed checksum", + str); + return (NULL); + } + + result = (isbn *) palloc(sizeof(isbn)); + + strncpy(result->num, str, 13); + memset(result->pad, ' ', 3); + return (result); +} + +/* + * The ISBN checksum is defined as follows: + * + * Number the digits from 1 to 9 (call this N). + * Compute the sum, S, of N * D_N. + * The check digit, C, is the value which satisfies the equation + * S + 10*C === 0 (mod 11) + * The value 10 for C is written as `X'. + * + * For our purposes, we want the complete sum including the check + * digit; if this is zero, then the checksum passed. We also check + * the syntactic validity if the provided string, and return 12 + * if any errors are found. + */ +int4 +isbn_sum(char *str) +{ + int4 sum = 0, dashes = 0, val; + int i; + + for (i = 0; str[i] && i < 13; i++) { + switch(str[i]) { + case '-': + if (++dashes > 3) + return 12; + continue; + + case '0': case '1': case '2': case '3': + case '4': case '5': case '6': case '7': + case '8': case '9': + val = str[i] - '0'; + break; + + case 'X': case 'x': + val = 10; + break; + + default: + return 12; + } + + sum += val * (i + 1 - dashes); + } + return (sum % 11); +} + +/* + * ISBN output function. + */ + +char * +isbn_out(isbn * num) +{ + char *result; + + if (num == NULL) + return (NULL); + + result = (char *) palloc(14); + + result[0] = '\0'; + strncat(result, num->num, 13); + return (result); +} + +/* + * Boolean tests for magnitude. + */ + +bool +isbn_lt(isbn * a1, isbn * a2) +{ + return (strncmp(a1->num, a2->num, 13) < 0); +}; + +bool +isbn_le(isbn * a1, isbn * a2) +{ + return (strncmp(a1->num, a2->num, 13) <= 0); +}; + +bool +isbn_eq(isbn * a1, isbn * a2) +{ + return (strncmp(a1->num, a2->num, 13) == 0); +}; + +bool +isbn_ge(isbn * a1, isbn * a2) +{ + return (strncmp(a1->num, a2->num, 13) >= 0); +}; + +bool +isbn_gt(isbn * a1, isbn * a2) +{ + return (strncmp(a1->num, a2->num, 13) > 0); +}; + +bool +isbn_ne(isbn * a1, isbn * a2) +{ + return (strncmp(a1->num, a2->num, 13) != 0); +}; + +/* + * Comparison function for sorting: + */ + +int4 +isbn_cmp(isbn * a1, isbn * a2) +{ + return (strncmp(a1->num, a2->num, 13)); +} + +/* + * eof + */ diff --git a/contrib/isbn_issn/isbn.sql b/contrib/isbn_issn/isbn.sql new file mode 100644 index 00000000000..4889aafbd40 --- /dev/null +++ b/contrib/isbn_issn/isbn.sql @@ -0,0 +1,116 @@ +-- +-- PostgreSQL code for ISBNs. +-- +-- $Id: isbn.sql,v 1.1 1998/08/17 03:35:05 scrappy Exp $ +-- + +load '/usr/local/pgsql/modules/isbn.so'; + +-- +-- Input and output functions and the type itself: +-- + +create function isbn_in(opaque) + returns opaque + as '/usr/local/pgsql/modules/isbn.so' + language 'c'; + +create function isbn_out(opaque) + returns opaque + as '/usr/local/pgsql/modules/isbn.so' + language 'c'; + +create type isbn ( + internallength = 16, + externallength = 13, + input = isbn_in, + output = isbn_out +); + +-- +-- The various boolean tests: +-- + +create function isbn_lt(isbn, isbn) + returns bool + as '/usr/local/pgsql/modules/isbn.so' + language 'c'; + +create function isbn_le(isbn, isbn) + returns bool + as '/usr/local/pgsql/modules/isbn.so' + language 'c'; + +create function isbn_eq(isbn, isbn) + returns bool + as '/usr/local/pgsql/modules/isbn.so' + language 'c'; + +create function isbn_ge(isbn, isbn) + returns bool + as '/usr/local/pgsql/modules/isbn.so' + language 'c'; + +create function isbn_gt(isbn, isbn) + returns bool + as '/usr/local/pgsql/modules/isbn.so' + language 'c'; + +create function isbn_ne(isbn, isbn) + returns bool + as '/usr/local/pgsql/modules/isbn.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 = isbn, + rightarg = isbn, +-- negator = >=, + procedure = isbn_lt +); + +create operator <= ( + leftarg = isbn, + rightarg = isbn, +-- negator = >, + procedure = isbn_le +); + +create operator = ( + leftarg = isbn, + rightarg = isbn, + commutator = =, +-- negator = <>, + procedure = isbn_eq +); + +create operator >= ( + leftarg = isbn, + rightarg = isbn, + negator = <, + procedure = isbn_ge +); + +create operator > ( + leftarg = isbn, + rightarg = isbn, + negator = <=, + procedure = isbn_gt +); + +create operator <> ( + leftarg = isbn, + rightarg = isbn, + negator = =, + procedure = isbn_ne +); + +-- +-- eof +-- diff --git a/contrib/isbn_issn/issn.c b/contrib/isbn_issn/issn.c new file mode 100644 index 00000000000..8f5aa22f7c4 --- /dev/null +++ b/contrib/isbn_issn/issn.c @@ -0,0 +1,178 @@ +/* + * PostgreSQL type definitions for ISSNs. + * + * $Id: issn.c,v 1.1 1998/08/17 03:35:05 scrappy Exp $ + */ + +#include + +#include +#include + +/* + * This is the internal storage format for ISSNs. + * NB: This is an intentional type pun with builtin type `char16'. + */ + +typedef struct issn +{ + char num[9]; + char pad[7]; +} issn; + +/* + * Various forward declarations: + */ + +issn *issn_in(char *str); +char *issn_out(issn * addr); + +bool issn_lt(issn * a1, issn * a2); +bool issn_le(issn * a1, issn * a2); +bool issn_eq(issn * a1, issn * a2); +bool issn_ge(issn * a1, issn * a2); +bool issn_gt(issn * a1, issn * a2); + +bool issn_ne(issn * a1, issn * a2); + +int4 issn_cmp(issn * a1, issn * a2); + +int4 issn_sum(char *str); + +/* + * ISSN reader. + */ + +issn * +issn_in(char *str) +{ + issn *result; + char *cp; + int count; + + if (strlen(str) != 9) { + elog(ERROR, "issn_in: invalid ISSN \"%s\"", str); + return (NULL); + } + if (issn_sum(str) != 0) { + elog(ERROR, "issn_in: purported ISSN \"%s\" failed checksum", + str); + return (NULL); + } + + result = (issn *) palloc(sizeof(issn)); + + strncpy(result->num, str, 9); + memset(result->pad, ' ', 7); + return (result); +} + +/* + * The ISSN checksum works just like the ISBN sum, only different + * (of course!). + * Here, the weights start at 8 and decrease. + */ +int4 +issn_sum(char *str) +{ + int4 sum = 0, dashes = 0, val; + int i; + + for (i = 0; str[i] && i < 9; i++) { + switch(str[i]) { + case '-': + if (++dashes > 1) + return 12; + continue; + + case '0': case '1': case '2': case '3': + case '4': case '5': case '6': case '7': + case '8': case '9': + val = str[i] - '0'; + break; + + case 'X': case 'x': + val = 10; + break; + + default: + return 12; + } + + sum += val * (8 - (i - dashes)); + } + return (sum % 11); +} + +/* + * ISSN output function. + */ + +char * +issn_out(issn * num) +{ + char *result; + + if (num == NULL) + return (NULL); + + result = (char *) palloc(14); + + result[0] = '\0'; + strncat(result, num->num, 9); + return (result); +} + +/* + * Boolean tests for magnitude. + */ + +bool +issn_lt(issn * a1, issn * a2) +{ + return (strncmp(a1->num, a2->num, 9) < 0); +}; + +bool +issn_le(issn * a1, issn * a2) +{ + return (strncmp(a1->num, a2->num, 9) <= 0); +}; + +bool +issn_eq(issn * a1, issn * a2) +{ + return (strncmp(a1->num, a2->num, 9) == 0); +}; + +bool +issn_ge(issn * a1, issn * a2) +{ + return (strncmp(a1->num, a2->num, 9) >= 0); +}; + +bool +issn_gt(issn * a1, issn * a2) +{ + return (strncmp(a1->num, a2->num, 9) > 0); +}; + +bool +issn_ne(issn * a1, issn * a2) +{ + return (strncmp(a1->num, a2->num, 9) != 0); +}; + +/* + * Comparison function for sorting: + */ + +int4 +issn_cmp(issn * a1, issn * a2) +{ + return (strncmp(a1->num, a2->num, 9)); +} + +/* + * eof + */ diff --git a/contrib/isbn_issn/issn.sql b/contrib/isbn_issn/issn.sql new file mode 100644 index 00000000000..c1fac916248 --- /dev/null +++ b/contrib/isbn_issn/issn.sql @@ -0,0 +1,116 @@ +-- +-- PostgreSQL code for ISSNs. +-- +-- $Id: issn.sql,v 1.1 1998/08/17 03:35:05 scrappy Exp $ +-- + +load '/usr/local/pgsql/modules/issn.so'; + +-- +-- Input and output functions and the type itself: +-- + +create function issn_in(opaque) + returns opaque + as '/usr/local/pgsql/modules/issn.so' + language 'c'; + +create function issn_out(opaque) + returns opaque + as '/usr/local/pgsql/modules/issn.so' + language 'c'; + +create type issn ( + internallength = 16, + externallength = 9, + input = issn_in, + output = issn_out +); + +-- +-- The various boolean tests: +-- + +create function issn_lt(issn, issn) + returns bool + as '/usr/local/pgsql/modules/issn.so' + language 'c'; + +create function issn_le(issn, issn) + returns bool + as '/usr/local/pgsql/modules/issn.so' + language 'c'; + +create function issn_eq(issn, issn) + returns bool + as '/usr/local/pgsql/modules/issn.so' + language 'c'; + +create function issn_ge(issn, issn) + returns bool + as '/usr/local/pgsql/modules/issn.so' + language 'c'; + +create function issn_gt(issn, issn) + returns bool + as '/usr/local/pgsql/modules/issn.so' + language 'c'; + +create function issn_ne(issn, issn) + returns bool + as '/usr/local/pgsql/modules/issn.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 = issn, + rightarg = issn, +-- negator = >=, + procedure = issn_lt +); + +create operator <= ( + leftarg = issn, + rightarg = issn, +-- negator = >, + procedure = issn_le +); + +create operator = ( + leftarg = issn, + rightarg = issn, + commutator = =, +-- negator = <>, + procedure = issn_eq +); + +create operator >= ( + leftarg = issn, + rightarg = issn, + negator = <, + procedure = issn_ge +); + +create operator > ( + leftarg = issn, + rightarg = issn, + negator = <=, + procedure = issn_gt +); + +create operator <> ( + leftarg = issn, + rightarg = issn, + negator = =, + procedure = issn_ne +); + +-- +-- eof +--