Add contrib/isn module for ISBN/ISSN/EAN13/etc product numbers, and

remove the old isbn_issn module which is about to be obsoleted by EAN13.
contrib/isn is by Germán Méndez Bravo.  Our thanks to Garrett A. Wollman
for having written the original isbn_issn module.
This commit is contained in:
Tom Lane 2006-09-09 04:07:52 +00:00
parent 42c17a6bb0
commit dff84dc762
18 changed files with 4582 additions and 752 deletions

View File

@ -1,4 +1,4 @@
# $PostgreSQL: pgsql/contrib/Makefile,v 1.69 2006/09/05 18:00:57 teodor Exp $
# $PostgreSQL: pgsql/contrib/Makefile,v 1.70 2006/09/09 04:07:51 tgl Exp $
subdir = contrib
top_builddir = ..
@ -12,10 +12,10 @@ WANTED_DIRS = \
dblink \
earthdistance \
fuzzystrmatch \
hstore \
hstore \
intagg \
intarray \
isbn_issn \
isn \
lo \
ltree \
oid2name \

View File

@ -64,9 +64,9 @@ intarray -
Index support for arrays of int4, using GiST
by Teodor Sigaev <teodor@sigaev.ru> and Oleg Bartunov <oleg@sai.msu.su>
isbn_issn -
PostgreSQL type extensions for ISBN (books) and ISSN (serials)
by Garrett A. Wollman <wollman@khavrinen.lcs.mit.edu>
isn -
PostgreSQL type extensions for ISBN, ISSN, ISMN, EAN13 product numbers
by Germán Méndez Bravo (Kronuz) <kronuz@hotmail.com>
lo -
Large Object maintenance

View File

@ -1,16 +0,0 @@
# $PostgreSQL: pgsql/contrib/isbn_issn/Makefile,v 1.15 2006/02/27 12:54:39 petere Exp $
MODULES = isbn_issn
DATA_built = isbn_issn.sql
DATA = uninstall_isbn_issn.sql
DOCS = README.isbn_issn
ifdef USE_PGXS
PGXS := $(shell pg_config --pgxs)
include $(PGXS)
else
subdir = contrib/isbn_issn
top_builddir = ../..
include $(top_builddir)/src/Makefile.global
include $(top_srcdir)/contrib/contrib-global.mk
endif

View File

@ -1,22 +0,0 @@
ISBN (books) and ISSN (serials)
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
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

View File

@ -1,378 +0,0 @@
/*
* PostgreSQL type definitions for ISBNs.
*
* $PostgreSQL: pgsql/contrib/isbn_issn/isbn_issn.c,v 1.9 2006/05/30 22:12:13 tgl Exp $
*/
#include "postgres.h"
#include "fmgr.h"
PG_MODULE_MAGIC;
/*
* 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;
int len;
len = strlen(str);
if (len != 13)
ereport(ERROR,
(errcode(ERRCODE_INVALID_PARAMETER_VALUE),
errmsg("invalid ISBN: \"%s\"", str),
errdetail("ISBNs must be 13 characters in length.")));
if (isbn_sum(str) != 0)
ereport(ERROR,
(errcode(ERRCODE_INVALID_PARAMETER_VALUE),
errmsg("invalid ISBN: \"%s\"", str),
errdetail("ISBN failed checksum.")));
result = (isbn *) palloc(sizeof(isbn));
memcpy(result->num, str, len);
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));
}
/* ----------------------------- ISSN --------------------------- */
/*
* 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;
int len;
len = strlen(str);
if (len != 9)
ereport(ERROR,
(errcode(ERRCODE_INVALID_PARAMETER_VALUE),
errmsg("invalid ISSN: \"%s\"", str),
errdetail("ISSNs must be 9 characters in length.")));
if (issn_sum(str) != 0)
ereport(ERROR,
(errcode(ERRCODE_INVALID_PARAMETER_VALUE),
errmsg("invalid ISSN: \"%s\"", str),
errdetail("ISSN failed checksum.")));
result = (issn *) palloc(sizeof(issn));
memcpy(result->num, str, len);
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));
}

View File

@ -1,269 +0,0 @@
--
-- PostgreSQL code for ISSNs.
--
-- $PostgreSQL: pgsql/contrib/isbn_issn/isbn_issn.sql.in,v 1.12 2006/02/27 16:09:48 petere Exp $
--
-- Adjust this setting to control where the objects get created.
SET search_path = public;
--
-- Input and output functions and the type itself:
--
CREATE FUNCTION issn_in(cstring)
RETURNS issn
AS 'MODULE_PATHNAME'
LANGUAGE C STRICT;
CREATE FUNCTION issn_out(issn)
RETURNS cstring
AS 'MODULE_PATHNAME'
LANGUAGE C STRICT;
CREATE TYPE issn (
INTERNALLENGTH = 16,
EXTERNALLENGTH = 9,
INPUT = issn_in,
OUTPUT = issn_out
);
COMMENT ON TYPE issn
is 'International Standard Serial Number';
--
-- The various boolean tests:
--
CREATE FUNCTION issn_lt(issn, issn)
RETURNS bool
AS 'MODULE_PATHNAME'
LANGUAGE C STRICT;
CREATE FUNCTION issn_le(issn, issn)
RETURNS bool
AS 'MODULE_PATHNAME'
LANGUAGE C STRICT;
CREATE FUNCTION issn_eq(issn, issn)
RETURNS bool
AS 'MODULE_PATHNAME'
LANGUAGE C STRICT;
CREATE FUNCTION issn_ge(issn, issn)
RETURNS bool
AS 'MODULE_PATHNAME'
LANGUAGE C STRICT;
CREATE FUNCTION issn_gt(issn, issn)
RETURNS bool
AS 'MODULE_PATHNAME'
LANGUAGE C STRICT;
CREATE FUNCTION issn_ne(issn, issn)
RETURNS bool
AS 'MODULE_PATHNAME'
LANGUAGE C STRICT;
--
-- 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
);
-- Register 'issn' comparison function
CREATE FUNCTION issn_cmp(issn, issn)
RETURNS integer
AS '$libdir/isbn_issn'
LANGUAGE C STRICT;
-- Create default operator class for 'issn' --
-- Needed to create index or primary key --
CREATE OPERATOR CLASS issn_ops
DEFAULT FOR TYPE issn USING btree
AS
OPERATOR 1 < ,
OPERATOR 2 <= ,
OPERATOR 3 = ,
OPERATOR 4 >= ,
OPERATOR 5 > ,
FUNCTION 1 issn_cmp(issn, issn);
------------------------------------------------------------------------
--
-- Same code for ISBN
--
-- Input and output functions and the type itself:
--
CREATE FUNCTION isbn_in(cstring)
RETURNS isbn
AS 'MODULE_PATHNAME'
LANGUAGE C STRICT;
CREATE FUNCTION isbn_out(isbn)
RETURNS cstring
AS 'MODULE_PATHNAME'
LANGUAGE C STRICT;
CREATE TYPE isbn (
INTERNALLENGTH = 16,
EXTERNALLENGTH = 13,
INPUT = isbn_in,
OUTPUT = isbn_out
);
COMMENT ON TYPE isbn IS 'International Standard Book Number';
--
-- The various boolean tests:
--
CREATE FUNCTION isbn_lt(isbn, isbn)
RETURNS bool
AS 'MODULE_PATHNAME'
LANGUAGE C STRICT;
CREATE FUNCTION isbn_le(isbn, isbn)
RETURNS bool
AS 'MODULE_PATHNAME'
LANGUAGE C STRICT;
CREATE FUNCTION isbn_eq(isbn, isbn)
RETURNS bool
AS 'MODULE_PATHNAME'
LANGUAGE C STRICT;
CREATE FUNCTION isbn_ge(isbn, isbn)
RETURNS bool
AS 'MODULE_PATHNAME'
LANGUAGE C STRICT;
CREATE FUNCTION isbn_gt(isbn, isbn)
RETURNS bool
AS 'MODULE_PATHNAME'
LANGUAGE C STRICT;
CREATE FUNCTION isbn_ne(isbn, isbn)
RETURNS bool
AS 'MODULE_PATHNAME'
LANGUAGE C STRICT;
--
-- 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
);
-- Register 'isbn' comparison function
CREATE FUNCTION isbn_cmp(isbn, isbn)
RETURNS integer
AS '$libdir/isbn_issn'
LANGUAGE C STRICT;
-- Create default operator class for 'isbn' --
-- Needed to create index or primary key --
CREATE OPERATOR CLASS isbn_ops
DEFAULT FOR TYPE isbn USING btree
AS
OPERATOR 1 < ,
OPERATOR 2 <= ,
OPERATOR 3 = ,
OPERATOR 4 >= ,
OPERATOR 5 > ,
FUNCTION 1 isbn_cmp(isbn, isbn);

View File

@ -1,61 +0,0 @@
SET search_path = public;
DROP OPERATOR CLASS isbn_ops USING btree;
DROP FUNCTION isbn_cmp(isbn, isbn);
DROP OPERATOR <> (isbn, isbn);
DROP OPERATOR > (isbn, isbn);
DROP OPERATOR >= (isbn, isbn);
DROP OPERATOR = (isbn, isbn);
DROP OPERATOR <= (isbn, isbn);
DROP OPERATOR < (isbn, isbn);
DROP FUNCTION isbn_ne(isbn, isbn);
DROP FUNCTION isbn_gt(isbn, isbn);
DROP FUNCTION isbn_ge(isbn, isbn);
DROP FUNCTION isbn_eq(isbn, isbn);
DROP FUNCTION isbn_le(isbn, isbn);
DROP FUNCTION isbn_lt(isbn, isbn);
DROP TYPE isbn CASCADE;
DROP OPERATOR CLASS issn_ops USING btree;
DROP FUNCTION issn_cmp(issn, issn);
DROP OPERATOR <> (issn, issn);
DROP OPERATOR > (issn, issn);
DROP OPERATOR >= (issn, issn);
DROP OPERATOR = (issn, issn);
DROP OPERATOR <= (issn, issn);
DROP OPERATOR < (issn, issn);
DROP FUNCTION issn_ne(issn, issn);
DROP FUNCTION issn_gt(issn, issn);
DROP FUNCTION issn_ge(issn, issn);
DROP FUNCTION issn_eq(issn, issn);
DROP FUNCTION issn_le(issn, issn);
DROP FUNCTION issn_lt(issn, issn);
DROP TYPE issn CASCADE;

147
contrib/isn/EAN13.h Normal file
View File

@ -0,0 +1,147 @@
/*
* EAN13.h
* PostgreSQL type definitions for ISNs (ISBN, ISMN, ISSN, EAN13, UPC)
*
* Information recompiled by Kronuz on August 23, 2006
* http://www.gs1.org/productssolutions/idkeys/support/prefix_list.html
*
* IDENTIFICATION
* $PostgreSQL: pgsql/contrib/isn/EAN13.h,v 1.1 2006/09/09 04:07:52 tgl Exp $
*
*/
/* where the digit set begins, and how many of them are in the table */
const unsigned EAN13_index[10][2] = {
{0, 6},
{6, 1},
{7, 1},
{8, 5},
{13, 20},
{33, 15},
{48, 19},
{67, 23},
{90, 17},
{107, 12},
};
const char *EAN13_range[][2] = {
{"000", "019"}, /* GS1 US */
{"020", "029"}, /* Restricted distribution (MO defined) */
{"030", "039"}, /* GS1 US */
{"040", "049"}, /* Restricted distribution (MO defined) */
{"050", "059"}, /* Coupons */
{"060", "099"}, /* GS1 US */
{"100", "139"}, /* GS1 US */
{"200", "299"}, /* Restricted distribution (MO defined) */
{"300", "379"}, /* GS1 France */
{"380", "380"}, /* GS1 Bulgaria */
{"383", "383"}, /* GS1 Slovenija */
{"385", "385"}, /* GS1 Croatia */
{"387", "387"}, /* GS1 BIH (Bosnia-Herzegovina) */
{"400", "440"}, /* GS1 Germany */
{"450", "459"}, /* GS1 Japan */
{"460", "469"}, /* GS1 Russia */
{"470", "470"}, /* GS1 Kyrgyzstan */
{"471", "471"}, /* GS1 Taiwan */
{"474", "474"}, /* GS1 Estonia */
{"475", "475"}, /* GS1 Latvia */
{"476", "476"}, /* GS1 Azerbaijan */
{"477", "477"}, /* GS1 Lithuania */
{"478", "478"}, /* GS1 Uzbekistan */
{"479", "479"}, /* GS1 Sri Lanka */
{"480", "480"}, /* GS1 Philippines */
{"481", "481"}, /* GS1 Belarus */
{"482", "482"}, /* GS1 Ukraine */
{"484", "484"}, /* GS1 Moldova */
{"485", "485"}, /* GS1 Armenia */
{"486", "486"}, /* GS1 Georgia */
{"487", "487"}, /* GS1 Kazakstan */
{"489", "489"}, /* GS1 Hong Kong */
{"490", "499"}, /* GS1 Japan */
{"500", "509"}, /* GS1 UK */
{"520", "520"}, /* GS1 Greece */
{"528", "528"}, /* GS1 Lebanon */
{"529", "529"}, /* GS1 Cyprus */
{"530", "530"}, /* GS1 Albania */
{"531", "531"}, /* GS1 MAC (FYR Macedonia) */
{"535", "535"}, /* GS1 Malta */
{"539", "539"}, /* GS1 Ireland */
{"540", "549"}, /* GS1 Belgium & Luxembourg */
{"560", "560"}, /* GS1 Portugal */
{"569", "569"}, /* GS1 Iceland */
{"570", "579"}, /* GS1 Denmark */
{"590", "590"}, /* GS1 Poland */
{"594", "594"}, /* GS1 Romania */
{"599", "599"}, /* GS1 Hungary */
{"600", "601"}, /* GS1 South Africa */
{"603", "603"}, /* GS1 Ghana */
{"608", "608"}, /* GS1 Bahrain */
{"609", "609"}, /* GS1 Mauritius */
{"611", "611"}, /* GS1 Morocco */
{"613", "613"}, /* GS1 Algeria */
{"616", "616"}, /* GS1 Kenya */
{"618", "618"}, /* GS1 Ivory Coast */
{"619", "619"}, /* GS1 Tunisia */
{"621", "621"}, /* GS1 Syria */
{"622", "622"}, /* GS1 Egypt */
{"624", "624"}, /* GS1 Libya */
{"625", "625"}, /* GS1 Jordan */
{"626", "626"}, /* GS1 Iran */
{"627", "627"}, /* GS1 Kuwait */
{"628", "628"}, /* GS1 Saudi Arabia */
{"629", "629"}, /* GS1 Emirates */
{"640", "649"}, /* GS1 Finland */
{"690", "695"}, /* GS1 China */
{"700", "709"}, /* GS1 Norway */
{"729", "729"}, /* GS1 Israel */
{"730", "739"}, /* GS1 Sweden */
{"740", "740"}, /* GS1 Guatemala */
{"741", "741"}, /* GS1 El Salvador */
{"742", "742"}, /* GS1 Honduras */
{"743", "743"}, /* GS1 Nicaragua */
{"744", "744"}, /* GS1 Costa Rica */
{"745", "745"}, /* GS1 Panama */
{"746", "746"}, /* GS1 Republica Dominicana */
{"750", "750"}, /* GS1 Mexico */
{"754", "755"}, /* GS1 Canada */
{"759", "759"}, /* GS1 Venezuela */
{"760", "769"}, /* GS1 Schweiz, Suisse, Svizzera */
{"770", "770"}, /* GS1 Colombia */
{"773", "773"}, /* GS1 Uruguay */
{"775", "775"}, /* GS1 Peru */
{"777", "777"}, /* GS1 Bolivia */
{"779", "779"}, /* GS1 Argentina */
{"780", "780"}, /* GS1 Chile */
{"784", "784"}, /* GS1 Paraguay */
{"786", "786"}, /* GS1 Ecuador */
{"789", "790"}, /* GS1 Brasil */
{"800", "839"}, /* GS1 Italy */
{"840", "849"}, /* GS1 Spain */
{"850", "850"}, /* GS1 Cuba */
{"858", "858"}, /* GS1 Slovakia */
{"859", "859"}, /* GS1 Czech */
{"860", "860"}, /* GS1 YU (Serbia & Montenegro) */
{"865", "865"}, /* GS1 Mongolia */
{"867", "867"}, /* GS1 North Korea */
{"869", "869"}, /* GS1 Turkey */
{"870", "879"}, /* GS1 Netherlands */
{"880", "880"}, /* GS1 South Korea */
{"884", "884"}, /* GS1 Cambodia */
{"885", "885"}, /* GS1 Thailand */
{"888", "888"}, /* GS1 Singapore */
{"890", "890"}, /* GS1 India */
{"893", "893"}, /* GS1 Vietnam */
{"899", "899"}, /* GS1 Indonesia */
{"900", "919"}, /* GS1 Austria */
{"930", "939"}, /* GS1 Australia */
{"940", "949"}, /* GS1 New Zealand */
{"950", "950"}, /* GS1 Head Office */
{"955", "955"}, /* GS1 Malaysia */
{"958", "958"}, /* GS1 Macau */
{"977", "977"}, /* Serial publications (ISSN) */
{"978", "978"}, /* Bookland (ISBN) */
{"979", "979"}, /* International Standard Music Number (ISMN) and ISBN contingent */
{"980", "980"}, /* Refund receipts */
{"981", "982"}, /* Common Currency Coupons */
{"990", "999"}, /* Coupons */
{NULL, NULL}
};

732
contrib/isn/ISBN.h Normal file
View File

@ -0,0 +1,732 @@
/*
* ISBN.h
* PostgreSQL type definitions for ISNs (ISBN, ISMN, ISSN, EAN13, UPC)
*
* Information recompiled by Kronuz on June 20, 2006
* http://www.isbn-international.org/
* http://www.isbn.org/
*
* IDENTIFICATION
* $PostgreSQL: pgsql/contrib/isn/ISBN.h,v 1.1 2006/09/09 04:07:52 tgl Exp $
*
* 0-393-04002-X => 039304002(X) <=> 039304002 <=> (978)039304002 <=> 978039304002(9) <=> 978-0-393-04002-9
*
*
* ISBN 0 3 9 3 0 4 0 0 2
* Weight 10 9 8 7 6 5 4 3 2
* Product 0 + 27 + 72 + 21 + 0 + 20 + 0 + 0 + 4 = 144
* 144 / 11 = 13 remainder 1
* Check digit 11 - 1 = 10 = X
* => 0-393-04002-X
*
* ISBN 9 7 8 0 3 9 3 0 4 0 0 2
* Weight 1 3 1 3 1 3 1 3 1 3 1 3
* Product 9 + 21 + 8 + 0 + 3 + 27 + 3 + 0 + 4 + 0 + 0 + 6 = 81
* 81 / 10 = 8 remainder 1
* Check digit 10 - 1 = 9
* => 978-0-393-04002-9
*
*/
/* where the digit set begins, and how many of them are in the table */
const unsigned ISBN_index[10][2] = {
{0, 6},
{6, 6},
{12, 8},
{20, 10},
{30, 6},
{36, 12},
{48, 0},
{48, 5},
{53, 59},
{112, 573},
};
const char *ISBN_range[][2] = {
{"0-00", "0-19"},
{"0-200", "0-699"},
{"0-7000", "0-8499"},
{"0-85000", "0-89999"},
{"0-900000", "0-949999"},
{"0-9500000", "0-9999999"},
{"1-00", "1-09"},
{"1-100", "1-399"},
{"1-4000", "1-5499"},
{"1-55000", "1-86979"},
{"1-869800", "1-998999"},
{"1-9990000", "1-9999999"},
{"2-00", "2-19"},
{"2-200", "2-349"},
{"2-35000", "2-39999"},
{"2-400", "2-699"},
{"2-7000", "2-8399"},
{"2-84000", "2-89999"},
{"2-900000", "2-949999"},
{"2-9500000", "2-9999999"},
{"3-00", "3-02"},
{"3-030", "3-033"},
{"3-0340", "3-0369"},
{"3-03700", "3-03999"},
{"3-04", "3-19"},
{"3-200", "3-699"},
{"3-7000", "3-8499"},
{"3-85000", "3-89999"},
{"3-900000", "3-949999"},
{"3-9500000", "3-9999999"},
{"4-00", "4-19"},
{"4-200", "4-699"},
{"4-7000", "4-8499"},
{"4-85000", "4-89999"},
{"4-900000", "4-949999"},
{"4-9500000", "4-9999999"},
{"5-00", "5-19"},
{"5-200", "5-699"},
{"5-7000", "5-8499"},
{"5-85000", "5-89999"},
{"5-900000", "5-909999"},
{"5-91000", "5-91999"},
{"5-9200", "5-9299"},
{"5-93000", "5-94999"},
{"5-9500", "5-9799"},
{"5-98000", "5-98999"},
{"5-9900000", "5-9909999"},
{"5-9910", "5-9999"},
{"7-00", "7-09"},
{"7-100", "7-499"},
{"7-5000", "7-7999"},
{"7-80000", "7-89999"},
{"7-900000", "7-999999"},
{"80-00", "80-19"},
{"80-200", "80-699"},
{"80-7000", "80-8499"},
{"80-85000", "80-89999"},
{"80-900000", "80-999999"},
{"81-00", "81-19"},
{"81-200", "81-699"},
{"81-7000", "81-8499"},
{"81-85000", "81-89999"},
{"81-900000", "81-999999"},
{"82-00", "82-19"},
{"82-200", "82-699"},
{"82-7000", "82-8999"},
{"82-90000", "82-98999"},
{"82-990000", "82-999999"},
{"83-00", "83-19"},
{"83-200", "83-599"},
{"83-60000", "83-69999"},
{"83-7000", "83-8499"},
{"83-85000", "83-89999"},
{"83-900000", "83-999999"},
{"84-00", "84-19"},
{"84-200", "84-699"},
{"84-7000", "84-8499"},
{"84-85000", "84-89999"},
{"84-9000", "84-9199"},
{"84-920000", "84-923999"},
{"84-92400", "84-92999"},
{"84-930000", "84-949999"},
{"84-95000", "84-96999"},
{"84-9700", "84-9999"},
{"85-00", "85-19"},
{"85-200", "85-599"},
{"85-60000", "85-69999"},
{"85-7000", "85-8499"},
{"85-85000", "85-89999"},
{"85-900000", "85-979999"},
{"85-98000", "85-99999"},
{"86-00", "86-29"},
{"86-300", "86-699"},
{"86-7000", "86-7999"},
{"86-80000", "86-89999"},
{"86-900000", "86-999999"},
{"87-00", "87-29"},
{"87-400", "87-649"},
{"87-7000", "87-7999"},
{"87-85000", "87-94999"},
{"87-970000", "87-999999"},
{"88-00", "88-19"},
{"88-200", "88-599"},
{"88-6000", "88-8499"},
{"88-85000", "88-89999"},
{"88-900000", "88-949999"},
{"88-95000", "88-99999"},
{"89-00", "89-24"},
{"89-250", "89-549"},
{"89-5500", "89-8499"},
{"89-85000", "89-94999"},
{"89-950000", "89-999999"},
{"90-00", "90-19"},
{"90-200", "90-499"},
{"90-5000", "90-6999"},
{"90-70000", "90-79999"},
{"90-800000", "90-849999"},
{"90-8500", "90-8999"},
{"90-900000", "90-909999"},
{"90-940000", "90-949999"},
{"91-0", "91-1"},
{"91-20", "91-49"},
{"91-500", "91-649"},
{"91-7000", "91-7999"},
{"91-85000", "91-94999"},
{"91-970000", "91-999999"},
{"92-0", "92-5"},
{"92-60", "92-79"},
{"92-800", "92-899"},
{"92-9000", "92-9499"},
{"92-95000", "92-98999"},
{"92-990000", "92-999999"},
{"950-00", "950-49"},
{"950-500", "950-899"},
{"950-9000", "950-9899"},
{"950-99000", "950-99999"},
{"951-0", "951-1"},
{"951-20", "951-54"},
{"951-550", "951-889"},
{"951-8900", "951-9499"},
{"951-95000", "951-99999"},
{"952-00", "952-19"},
{"952-200", "952-499"},
{"952-5000", "952-5999"},
{"952-60", "952-65"},
{"952-6600", "952-6699"},
{"952-67000", "952-69999"},
{"952-7000", "952-7999"},
{"952-89", "952-94"},
{"952-9500", "952-9899"},
{"952-99000", "952-99999"},
{"953-0", "953-0"},
{"953-10", "953-14"},
{"953-150", "953-599"},
{"953-6000", "953-9499"},
{"953-95000", "953-99999"},
{"954-00", "954-29"},
{"954-300", "954-799"},
{"954-8000", "954-8999"},
{"954-90000", "954-92999"},
{"954-9300", "954-9999"},
{"955-0", "955-0"},
{"955-1000", "955-1999"},
{"955-20", "955-54"},
{"955-550", "955-799"},
{"955-8000", "955-9499"},
{"955-95000", "955-99999"},
{"956-00", "956-19"},
{"956-200", "956-699"},
{"956-7000", "956-9999"},
{"957-00", "957-02"},
{"957-0300", "957-0499"},
{"957-05", "957-19"},
{"957-2000", "957-2099"},
{"957-21", "957-27"},
{"957-28000", "957-30999"},
{"957-31", "957-43"},
{"957-440", "957-819"},
{"957-8200", "957-9699"},
{"957-97000", "957-99999"},
{"958-00", "958-59"},
{"958-600", "958-799"},
{"958-8000", "958-9499"},
{"958-95000", "958-99999"},
{"959-00", "959-19"},
{"959-200", "959-699"},
{"959-7000", "959-8499"},
{"960-00", "960-19"},
{"960-200", "960-659"},
{"960-6600", "960-6899"},
{"960-690", "960-699"},
{"960-7000", "960-8499"},
{"960-85000", "960-99999"},
{"961-00", "961-19"},
{"961-200", "961-599"},
{"961-6000", "961-8999"},
{"961-90000", "961-94999"},
{"962-00", "962-19"},
{"962-200", "962-699"},
{"962-7000", "962-8499"},
{"962-85000", "962-86999"},
{"962-8700", "962-8999"},
{"962-900", "962-999"},
{"963-00", "963-19"},
{"963-200", "963-699"},
{"963-7000", "963-8499"},
{"963-85000", "963-89999"},
{"963-9000", "963-9999"},
{"964-00", "964-14"},
{"964-150", "964-249"},
{"964-2500", "964-2999"},
{"964-300", "964-549"},
{"964-5500", "964-8999"},
{"964-90000", "964-96999"},
{"964-970", "964-989"},
{"964-9900", "964-9999"},
{"965-00", "965-19"},
{"965-200", "965-599"},
{"965-7000", "965-7999"},
{"965-90000", "965-99999"},
{"966-00", "966-28"},
{"966-2900", "966-2999"},
{"966-300", "966-699"},
{"966-7000", "966-8999"},
{"966-90000", "966-99999"},
{"967-0", "967-5"},
{"967-60", "967-89"},
{"967-900", "967-989"},
{"967-9900", "967-9989"},
{"967-99900", "967-99999"},
{"968-01", "968-39"},
{"968-400", "968-499"},
{"968-5000", "968-7999"},
{"968-800", "968-899"},
{"968-9000", "968-9999"},
{"969-0", "969-1"},
{"969-20", "969-39"},
{"969-400", "969-799"},
{"969-8000", "969-9999"},
{"970-01", "970-59"},
{"970-600", "970-899"},
{"970-9000", "970-9099"},
{"970-91000", "970-96999"},
{"970-9700", "970-9999"},
{"971-000", "971-019"},
{"971-02", "971-02"},
{"971-0300", "971-0599"},
{"971-06", "971-09"},
{"971-10", "971-49"},
{"971-500", "971-849"},
{"971-8500", "971-9099"},
{"971-91000", "971-99999"},
{"972-0", "972-1"},
{"972-20", "972-54"},
{"972-550", "972-799"},
{"972-8000", "972-9499"},
{"972-95000", "972-99999"},
{"973-0", "973-0"},
{"973-100", "973-199"},
{"973-20", "973-54"},
{"973-550", "973-759"},
{"973-7600", "973-8499"},
{"973-85000", "973-88999"},
{"973-8900", "973-9499"},
{"973-95000", "973-99999"},
{"974-00", "974-19"},
{"974-200", "974-699"},
{"974-7000", "974-8499"},
{"974-85000", "974-89999"},
{"974-90000", "974-94999"},
{"974-9500", "974-9999"},
{"975-00000", "975-00999"},
{"975-01", "975-24"},
{"975-250", "975-599"},
{"975-6000", "975-9199"},
{"975-92000", "975-98999"},
{"975-990", "975-999"},
{"976-0", "976-3"},
{"976-40", "976-59"},
{"976-600", "976-799"},
{"976-8000", "976-9499"},
{"976-95000", "976-99999"},
{"977-00", "977-19"},
{"977-200", "977-499"},
{"977-5000", "977-6999"},
{"977-700", "977-999"},
{"978-000", "978-199"},
{"978-2000", "978-2999"},
{"978-30000", "978-79999"},
{"978-8000", "978-8999"},
{"978-900", "978-999"},
{"979-000", "979-099"},
{"979-1000", "979-1499"},
{"979-15000", "979-19999"},
{"979-20", "979-29"},
{"979-3000", "979-3999"},
{"979-400", "979-799"},
{"979-8000", "979-9499"},
{"979-95000", "979-99999"},
{"980-00", "980-19"},
{"980-200", "980-599"},
{"980-6000", "980-9999"},
{"981-00", "981-19"},
{"981-200", "981-299"},
{"981-3000", "981-9999"},
{"982-00", "982-09"},
{"982-100", "982-699"},
{"982-70", "982-89"},
{"982-9000", "982-9999"},
{"983-00", "983-01"},
{"983-020", "983-199"},
{"983-2000", "983-3999"},
{"983-40000", "983-49999"},
{"983-50", "983-79"},
{"983-800", "983-899"},
{"983-9000", "983-9899"},
{"983-99000", "983-99999"},
{"984-00", "984-39"},
{"984-400", "984-799"},
{"984-8000", "984-8999"},
{"984-90000", "984-99999"},
{"985-00", "985-39"},
{"985-400", "985-599"},
{"985-6000", "985-8999"},
{"985-90000", "985-99999"},
{"986-00", "986-11"},
{"986-120", "986-559"},
{"986-5600", "986-7999"},
{"986-80000", "986-99999"},
{"987-00", "987-09"},
{"987-1000", "987-1999"},
{"987-20000", "987-29999"},
{"987-30", "987-49"},
{"987-500", "987-899"},
{"987-9000", "987-9499"},
{"987-95000", "987-99999"},
{"988-00", "988-19"},
{"988-200", "988-799"},
{"988-8000", "988-9699"},
{"988-97000", "988-99999"},
{"989-0", "989-1"},
{"989-20", "989-54"},
{"989-550", "989-799"},
{"989-8000", "989-9499"},
{"989-95000", "989-99999"},
{"9944-0", "9944-2"},
{"9944-300", "9944-499"},
{"9944-5000", "9944-5999"},
{"9944-60", "9944-89"},
{"9944-900", "9944-999"},
{"9945-00", "9945-39"},
{"9945-400", "9945-849"},
{"9945-8500", "9945-9999"},
{"9946-0", "9946-1"},
{"9946-20", "9946-39"},
{"9946-400", "9946-899"},
{"9946-9000", "9946-9999"},
{"9947-0", "9947-1"},
{"9947-20", "9947-79"},
{"9947-800", "9947-999"},
{"9948-00", "9948-39"},
{"9948-400", "9948-849"},
{"9948-8500", "9948-9999"},
{"9949-0", "9949-0"},
{"9949-10", "9949-39"},
{"9949-400", "9949-899"},
{"9949-9000", "9949-9999"},
{"9950-00", "9950-29"},
{"9950-300", "9950-849"},
{"9950-8500", "9950-9999"},
{"9951-00", "9951-39"},
{"9951-400", "9951-849"},
{"9951-8500", "9951-9999"},
{"9952-0", "9952-1"},
{"9952-20", "9952-39"},
{"9952-400", "9952-799"},
{"9952-8000", "9952-9999"},
{"9953-0", "9953-0"},
{"9953-10", "9953-39"},
{"9953-400", "9953-599"},
{"9953-60", "9953-89"},
{"9953-9000", "9953-9999"},
{"9954-0", "9954-1"},
{"9954-20", "9954-39"},
{"9954-400", "9954-799"},
{"9954-8000", "9954-9999"},
{"9955-00", "9955-39"},
{"9955-400", "9955-929"},
{"9955-9300", "9955-9999"},
{"9956-0", "9956-0"},
{"9956-10", "9956-39"},
{"9956-400", "9956-899"},
{"9956-9000", "9956-9999"},
{"9957-00", "9957-39"},
{"9957-400", "9957-849"},
{"9957-8500", "9957-9999"},
{"9958-0", "9958-0"},
{"9958-10", "9958-49"},
{"9958-500", "9958-899"},
{"9958-9000", "9958-9999"},
{"9959-0", "9959-1"},
{"9959-20", "9959-79"},
{"9959-800", "9959-949"},
{"9959-9500", "9959-9999"},
{"9960-00", "9960-59"},
{"9960-600", "9960-899"},
{"9960-9000", "9960-9999"},
{"9961-0", "9961-2"},
{"9961-30", "9961-69"},
{"9961-700", "9961-949"},
{"9961-9500", "9961-9999"},
{"9962-00", "9962-54"},
{"9962-5500", "9962-5599"},
{"9962-56", "9962-59"},
{"9962-600", "9962-849"},
{"9962-8500", "9962-9999"},
{"9963-0", "9963-2"},
{"9963-30", "9963-54"},
{"9963-550", "9963-749"},
{"9963-7500", "9963-9999"},
{"9964-0", "9964-6"},
{"9964-70", "9964-94"},
{"9964-950", "9964-999"},
{"9965-00", "9965-39"},
{"9965-400", "9965-899"},
{"9965-9000", "9965-9999"},
{"9966-00", "9966-69"},
{"9966-7000", "9966-7499"},
{"9966-750", "9966-959"},
{"9966-9600", "9966-9999"},
{"9967-00", "9967-39"},
{"9967-400", "9967-899"},
{"9967-9000", "9967-9999"},
{"9968-00", "9968-49"},
{"9968-500", "9968-939"},
{"9968-9400", "9968-9999"},
{"9970-00", "9970-39"},
{"9970-400", "9970-899"},
{"9970-9000", "9970-9999"},
{"9971-0", "9971-5"},
{"9971-60", "9971-89"},
{"9971-900", "9971-989"},
{"9971-9900", "9971-9999"},
{"9972-00", "9972-09"},
{"9972-1", "9972-1"},
{"9972-200", "9972-249"},
{"9972-2500", "9972-2999"},
{"9972-30", "9972-59"},
{"9972-600", "9972-899"},
{"9972-9000", "9972-9999"},
{"9973-0", "9973-0"},
{"9973-10", "9973-69"},
{"9973-700", "9973-969"},
{"9973-9700", "9973-9999"},
{"9974-0", "9974-2"},
{"9974-30", "9974-54"},
{"9974-550", "9974-749"},
{"9974-7500", "9974-9499"},
{"9974-95", "9974-99"},
{"9975-0", "9975-0"},
{"9975-100", "9975-399"},
{"9975-4000", "9975-4499"},
{"9975-45", "9975-89"},
{"9975-900", "9975-949"},
{"9975-9500", "9975-9999"},
{"9976-0", "9976-5"},
{"9976-60", "9976-89"},
{"9976-900", "9976-989"},
{"9976-9990", "9976-9999"},
{"9977-00", "9977-89"},
{"9977-900", "9977-989"},
{"9977-9900", "9977-9999"},
{"9978-00", "9978-29"},
{"9978-300", "9978-399"},
{"9978-40", "9978-94"},
{"9978-950", "9978-989"},
{"9978-9900", "9978-9999"},
{"9979-0", "9979-4"},
{"9979-50", "9979-75"},
{"9979-760", "9979-899"},
{"9979-9000", "9979-9999"},
{"9980-0", "9980-3"},
{"9980-40", "9980-89"},
{"9980-900", "9980-989"},
{"9980-9900", "9980-9999"},
{"9981-00", "9981-09"},
{"9981-100", "9981-159"},
{"9981-1600", "9981-1999"},
{"9981-20", "9981-79"},
{"9981-800", "9981-949"},
{"9981-9500", "9981-9999"},
{"9982-00", "9982-79"},
{"9982-800", "9982-989"},
{"9982-9900", "9982-9999"},
{"9983-80", "9983-94"},
{"9983-950", "9983-989"},
{"9983-9900", "9983-9999"},
{"9984-00", "9984-49"},
{"9984-500", "9984-899"},
{"9984-9000", "9984-9999"},
{"9985-0", "9985-4"},
{"9985-50", "9985-79"},
{"9985-800", "9985-899"},
{"9985-9000", "9985-9999"},
{"9986-00", "9986-39"},
{"9986-400", "9986-899"},
{"9986-9000", "9986-9399"},
{"9986-940", "9986-969"},
{"9986-97", "9986-99"},
{"9987-00", "9987-39"},
{"9987-400", "9987-879"},
{"9987-8800", "9987-9999"},
{"9988-0", "9988-2"},
{"9988-30", "9988-54"},
{"9988-550", "9988-749"},
{"9988-7500", "9988-9999"},
{"9989-0", "9989-0"},
{"9989-100", "9989-199"},
{"9989-2000", "9989-2999"},
{"9989-30", "9989-59"},
{"9989-600", "9989-949"},
{"9989-9500", "9989-9999"},
{"99901-00", "99901-49"},
{"99901-500", "99901-799"},
{"99901-80", "99901-99"},
{"99903-0", "99903-1"},
{"99903-20", "99903-89"},
{"99903-900", "99903-999"},
{"99904-0", "99904-5"},
{"99904-60", "99904-89"},
{"99904-900", "99904-999"},
{"99905-0", "99905-3"},
{"99905-40", "99905-79"},
{"99905-800", "99905-999"},
{"99906-0", "99906-2"},
{"99906-30", "99906-59"},
{"99906-600", "99906-699"},
{"99906-70", "99906-89"},
{"99906-9", "99906-9"},
{"99908-0", "99908-0"},
{"99908-10", "99908-89"},
{"99908-900", "99908-999"},
{"99909-0", "99909-3"},
{"99909-40", "99909-94"},
{"99909-950", "99909-999"},
{"99910-0", "99910-2"},
{"99910-30", "99910-89"},
{"99910-900", "99910-999"},
{"99911-00", "99911-59"},
{"99911-600", "99911-999"},
{"99912-0", "99912-3"},
{"99912-400", "99912-599"},
{"99912-60", "99912-89"},
{"99912-900", "99912-999"},
{"99913-0", "99913-2"},
{"99913-30", "99913-35"},
{"99913-600", "99913-604"},
{"99914-0", "99914-4"},
{"99914-50", "99914-89"},
{"99914-900", "99914-949"},
{"99915-0", "99915-4"},
{"99915-50", "99915-79"},
{"99915-800", "99915-999"},
{"99916-0", "99916-2"},
{"99916-30", "99916-69"},
{"99916-700", "99916-999"},
{"99917-0", "99917-2"},
{"99917-30", "99917-89"},
{"99917-900", "99917-999"},
{"99918-0", "99918-3"},
{"99918-40", "99918-79"},
{"99918-800", "99918-999"},
{"99919-0", "99919-2"},
{"99919-40", "99919-69"},
{"99919-900", "99919-999"},
{"99920-0", "99920-4"},
{"99920-50", "99920-89"},
{"99920-900", "99920-999"},
{"99921-0", "99921-1"},
{"99921-20", "99921-69"},
{"99921-700", "99921-799"},
{"99921-8", "99921-8"},
{"99921-90", "99921-99"},
{"99922-0", "99922-3"},
{"99922-40", "99922-69"},
{"99922-700", "99922-999"},
{"99923-0", "99923-1"},
{"99923-20", "99923-79"},
{"99923-800", "99923-999"},
{"99924-0", "99924-2"},
{"99924-30", "99924-79"},
{"99924-800", "99924-999"},
{"99925-0", "99925-3"},
{"99925-40", "99925-79"},
{"99925-800", "99925-999"},
{"99926-0", "99926-0"},
{"99926-10", "99926-59"},
{"99926-600", "99926-999"},
{"99927-0", "99927-2"},
{"99927-30", "99927-59"},
{"99927-600", "99927-999"},
{"99928-0", "99928-0"},
{"99928-10", "99928-79"},
{"99928-800", "99928-999"},
{"99929-0", "99929-4"},
{"99929-50", "99929-79"},
{"99929-800", "99929-999"},
{"99930-0", "99930-4"},
{"99930-50", "99930-79"},
{"99930-800", "99930-999"},
{"99931-0", "99931-4"},
{"99931-50", "99931-79"},
{"99931-800", "99931-999"},
{"99932-0", "99932-0"},
{"99932-10", "99932-59"},
{"99932-600", "99932-699"},
{"99932-7", "99932-7"},
{"99932-80", "99932-99"},
{"99933-0", "99933-2"},
{"99933-30", "99933-59"},
{"99933-600", "99933-999"},
{"99934-0", "99934-1"},
{"99934-20", "99934-79"},
{"99934-800", "99934-999"},
{"99935-0", "99935-2"},
{"99935-30", "99935-59"},
{"99935-600", "99935-799"},
{"99935-8", "99935-8"},
{"99935-90", "99935-99"},
{"99936-0", "99936-0"},
{"99936-10", "99936-59"},
{"99936-600", "99936-999"},
{"99937-0", "99937-1"},
{"99937-20", "99937-59"},
{"99937-600", "99937-999"},
{"99938-0", "99938-1"},
{"99938-20", "99938-59"},
{"99938-600", "99938-899"},
{"99938-90", "99938-99"},
{"99939-0", "99939-5"},
{"99939-60", "99939-89"},
{"99939-900", "99939-999"},
{"99940-0", "99940-0"},
{"99940-10", "99940-69"},
{"99940-700", "99940-999"},
{"99941-0", "99941-2"},
{"99941-30", "99941-89"},
{"99941-900", "99941-999"},
{"99942-0", "99942-4"},
{"99942-50", "99942-79"},
{"99942-800", "99942-999"},
{"99943-0", "99943-2"},
{"99943-30", "99943-59"},
{"99943-600", "99943-999"},
{"99944-0", "99944-4"},
{"99944-50", "99944-79"},
{"99944-800", "99944-999"},
{"99945-0", "99945-5"},
{"99945-60", "99945-89"},
{"99945-900", "99945-999"},
{"99946-0", "99946-2"},
{"99946-30", "99946-59"},
{"99946-600", "99946-999"},
{"99947-0", "99947-2"},
{"99947-30", "99947-69"},
{"99947-700", "99947-999"},
{"99948-0", "99948-4"},
{"99948-50", "99948-79"},
{"99948-800", "99948-999"},
{"99949-0", "99949-1"},
{"99949-20", "99949-89"},
{"99949-900", "99949-999"},
{"99950-0", "99950-4"},
{"99950-50", "99950-79"},
{"99950-800", "99950-999"},
{"99951-00", "99951-99"},
{"99952-0", "99952-4"},
{"99952-50", "99952-79"},
{"99952-800", "99952-999"},
{"99953-0", "99953-2"},
{"99953-30", "99953-79"},
{"99953-800", "99953-999"},
{"99954-0", "99954-2"},
{"99954-30", "99954-69"},
{"99954-700", "99954-999"},
{NULL, NULL},
};

52
contrib/isn/ISMN.h Normal file
View File

@ -0,0 +1,52 @@
/*
* ISMN.h
* PostgreSQL type definitions for ISNs (ISBN, ISMN, ISSN, EAN13, UPC)
*
* Information recompiled by Kronuz on November 12, 2004
* http://www.ismn-international.org
*
* IDENTIFICATION
* $PostgreSQL: pgsql/contrib/isn/ISMN.h,v 1.1 2006/09/09 04:07:52 tgl Exp $
*
* M-3452-4680-5 <=> (0)-3452-4680-5 <=> 0345246805 <=> 9790345246805 <=> 979-0-3452-4680-5
*
* (M counts as 3)
* ISMN M 3 4 5 2 4 6 8 0
* Weight 3 1 3 1 3 1 3 1 3
* Product 9 + 3 + 12 + 5 + 6 + 4 + 18 + 8 + 0 = 65
* 65 / 10 = 6 remainder 5
* Check digit 10 - 5 = 5
* => M-3452-4680-5
*
* ISMN 9 7 9 0 3 4 5 2 4 6 8 0
* Weight 1 3 1 3 1 3 1 3 1 3 1 3
* Product 9 + 21 + 9 + 0 + 3 + 12 + 5 + 6 + 4 + 18 + 8 + 0 = 95
* 95 / 10 = 9 remainder 5
* Check digit 10 - 5 = 5
* => 979-0-3452-4680-5
*
* Since mod10(9*1 + 7*3 + 9*1 + 0*3) = mod10(M*3) = mod10(3*3) = 9; the check digit remains the same.
*
*/
/* where the digit set begins, and how many of them are in the table */
const unsigned ISMN_index[10][2] = {
{0, 5},
{5, 0},
{5, 0},
{5, 0},
{5, 0},
{5, 0},
{5, 0},
{5, 0},
{5, 0},
{5, 0},
};
const char *ISMN_range[][2] = {
{"0-000", "0-099"},
{"0-1000", "0-3999"},
{"0-40000", "0-69999"},
{"0-700000", "0-899999"},
{"0-9000000", "0-9999999"},
{NULL, NULL}
};

49
contrib/isn/ISSN.h Normal file
View File

@ -0,0 +1,49 @@
/*
* ISSN.h
* PostgreSQL type definitions for ISNs (ISBN, ISMN, ISSN, EAN13, UPC)
*
* Information recompiled by Kronuz on November 12, 2004
* http://www.issn.org/
*
* IDENTIFICATION
* $PostgreSQL: pgsql/contrib/isn/ISSN.h,v 1.1 2006/09/09 04:07:52 tgl Exp $
*
* 1144-875X <=> 1144875(X) <=> 1144875 <=> (977)1144875 <=> 9771144875(00) <=> 977114487500(7) <=> 977-1144-875-00-7
*
*
* ISSN 1 1 4 4 8 7 5
* Weight 8 7 6 5 4 3 2
* Product 8 + 7 + 24 + 20 + 32 + 21 + 10 = 122
* 122 / 11 = 11 remainder 1
* Check digit 11 - 1 = 10 = X
* => 1144-875X
*
* ISSN 9 7 7 1 1 4 4 8 7 5 0 0
* Weight 1 3 1 3 1 3 1 3 1 3 1 3
* Product 9 + 21 + 7 + 3 + 1 + 12 + 4 + 24 + 7 + 15 + 0 + 0 = 103
* 103 / 10 = 10 remainder 3
* Check digit 10 - 3 = 7
* => 977-1144875-00-7 ?? <- suplemental number (number of the week, month, etc.)
* ^^ 00 for non-daily publications (01=Monday, 02=Tuesday, ...)
*
* The hyphenation is always in after the four digits of the ISSN code.
*
*/
/* where the digit set begins, and how many of them are in the table */
const unsigned ISSN_index[10][2] = {
{0, 1},
{0, 1},
{0, 1},
{0, 1},
{0, 1},
{0, 1},
{0, 1},
{0, 1},
{0, 1},
{0, 1},
};
const char *ISSN_range[][2] = {
{"0000-000", "9999-999"},
{NULL, NULL}
};

16
contrib/isn/Makefile Normal file
View File

@ -0,0 +1,16 @@
# $PostgreSQL: pgsql/contrib/isn/Makefile,v 1.1 2006/09/09 04:07:52 tgl Exp $
MODULES = isn
DATA_built = isn.sql
DATA = uninstall_isn.sql
DOCS = README.isn
ifdef USE_PGXS
PGXS = $(shell pg_config --pgxs)
include $(PGXS)
else
subdir = contrib/isn
top_builddir = ../..
include $(top_builddir)/src/Makefile.global
include $(top_srcdir)/contrib/contrib-global.mk
endif

217
contrib/isn/README.isn Normal file
View File

@ -0,0 +1,217 @@
EAN13 - UPC - ISBN (books) - ISMN (music) - ISSN (serials)
----------------------------------------------------------
Copyright Germán Méndez Bravo (Kronuz), 2004 - 2006
This module is released under the same BSD license as the rest of PostgreSQL.
The information to implement this module was collected through
several sites, including:
http://www.isbn-international.org/
http://www.issn.org/
http://www.ismn-international.org/
http://www.wikipedia.org/
the prefixes used for hyphenation where also compiled from:
http://www.gs1.org/productssolutions/idkeys/support/prefix_list.html
http://www.isbn-international.org/en/identifiers.html
http://www.ismn-international.org/ranges.html
Care was taken during the creation of the algorithms and they
were meticulously verified against the suggested algorithms
in the official ISBN, ISMN, ISSN User Manuals.
!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
THIS MODULE IS PROVIDED "AS IS" AND WITHOUT ANY WARRANTY
OF ANY KIND, EXPRESS OR IMPLIED.
!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
Content of the Module
-------------------------------------------------
This directory contains definitions for a few PostgreSQL
data types, for the following international-standard namespaces:
EAN13, UPC, ISBN (books), ISMN (music), and ISSN (serials). This module
is inspired by Garrett A. Wollman's isbn_issn code.
I wanted the database to fully validate numbers and also to use the
upcoming ISBN-13 and the EAN13 standards, as well as to have it
automatically doing hyphenations for ISBN numbers.
This new module validates, and automatically adds the correct
hyphenations to the numbers. Also, it supports the new ISBN-13
numbers to be used starting in January 2007.
Premises:
1. ISBN13, ISMN13, ISSN13 numbers are all EAN13 numbers
2. EAN13 numbers aren't always ISBN13, ISMN13 or ISSN13 (some are)
3. some ISBN13 numbers can be displayed as ISBN
4. some ISMN13 numbers can be displayed as ISMN
5. some ISSN13 numbers can be displayed as ISSN
6. all UPC, ISBN, ISMN and ISSN can be represented as EAN13 numbers
Note: All types are internally represented as 64 bit integers,
and internally all are consistently interchangeable.
We have the following data types:
+ EAN13 for European Article Numbers.
This type will always show the EAN13-display format.
Te output function for this is -> ean13_out()
+ ISBN13 for International Standard Book Numbers to be displayed in
the new EAN13-display format.
+ ISMN13 for International Standard Music Numbers to be displayed in
the new EAN13-display format.
+ ISSN13 for International Standard Serial Numbers to be displayed
in the new EAN13-display format.
These types will always display the long version of the ISxN (EAN13)
The output function to do this is -> ean13_out()
* The need for these types is just for displaying in different
ways the same data:
ISBN13 is actually the same as ISBN, ISMN13=ISMN and ISSN13=ISSN.
+ ISBN for International Standard Book Numbers to be displayed in
the current short-display format.
+ ISMN for International Standard Music Numbers to be displayed in
the current short-display format.
+ ISSN for International Standard Serial Numbers to be displayed
in the current short-display format.
These types will display the short version of the ISxN (ISxN 10)
whenever it's possible, and it will show ISxN 13 when it's
impossible to show the short version.
The output function to do this is -> isn_out()
+ UPC for Universal Product Codes.
UPC numbers are a subset of the EAN13 numbers (they are basically
EAN13 without the first '0' digit.)
The output function to do this is also -> isn_out()
We have the following input functions:
+ To take a string and return an EAN13 -> ean13_in()
+ To take a string and return valid ISBN or ISBN13 numbers -> isbn_in()
+ To take a string and return valid ISMN or ISMN13 numbers -> ismn_in()
+ To take a string and return valid ISSN or ISSN13 numbers -> issn_in()
+ To take a string and return an UPC codes -> upc_in()
We are able to cast from:
+ ISBN13 -> EAN13
+ ISMN13 -> EAN13
+ ISSN13 -> EAN13
+ ISBN -> EAN13
+ ISMN -> EAN13
+ ISSN -> EAN13
+ UPC -> EAN13
+ ISBN <-> ISBN13
+ ISMN <-> ISMN13
+ ISSN <-> ISSN13
We have two operator classes (for btree and for hash) so each data type
can be indexed for faster access.
The C API is implemented as:
extern Datum isn_out(PG_FUNCTION_ARGS);
extern Datum ean13_out(PG_FUNCTION_ARGS);
extern Datum ean13_in(PG_FUNCTION_ARGS);
extern Datum isbn_in(PG_FUNCTION_ARGS);
extern Datum ismn_in(PG_FUNCTION_ARGS);
extern Datum issn_in(PG_FUNCTION_ARGS);
extern Datum upc_in(PG_FUNCTION_ARGS);
On success:
+ isn_out() takes any of our types and returns a string containing
the shortes possible representation of the number.
+ ean13_out() takes any of our types and returns the
EAN13 (long) representation of the number.
+ ean13_in() takes a string and return a EAN13. Which, as stated in (2)
could or could not be any of our types, but it certainly is an EAN13
number. Only if the string is a valid EAN13 number, otherwise it fails.
+ isbn_in() takes a string and return an ISBN/ISBN13. Only if the string
is really a ISBN/ISBN13, otherwise it fails.
+ ismn_in() takes a string and return an ISMN/ISMN13. Only if the string
is really a ISMN/ISMN13, otherwise it fails.
+ issn_in() takes a string and return an ISSN/ISSN13. Only if the string
is really a ISSN/ISSN13, otherwise it fails.
+ upc_in() takes a string and return an UPC. Only if the string is
really a UPC, otherwise it fails.
(on failure, the functions 'ereport' the error)
Testing/Playing Functions
-------------------------------------------------
isn_weak(boolean) - Sets the weak input mode.
This function is intended for testing use only!
isn_weak() gets the current status of the weak mode.
"Weak" mode is used to be able to insert "invalid" data to a table.
"Invalid" as in the check digit being wrong, not missing numbers.
Why would you want to use the weak mode? well, it could be that
you have a huge collection of ISBN numbers, and that there are so many of
them that for weird reasons some have the wrong check digit (perhaps the
numbers where scanned from a printed list and the OCR got the numbers wrong,
perhaps the numbers were manually captured... who knows.) Anyway, the thing
is you might want to clean the mess up, but you still want to be able to have
all the numbers in your database and maybe use an external tool to access
the invalid numbers in the database so you can verify the information and
validate it more easily; as selecting all the invalid numbers in the table.
When you insert invalid numbers in a table using the weak mode, the number
will be inserted with the corrected check digit, but it will be flagged
with an exclamation mark ('!') at the end (i.e. 0-11-000322-5!)
You can also force the insertion of invalid numbers even not in the weak mode,
appending the '!' character at the end of the number.
To work with invalid numbers, you can use two functions:
+ make_valid(), which validates an invalid number (deleting the invalid flag)
+ is_valid(), which checks for the invalid flag presence.
Examples of Use
-------------------------------------------------
--Using the types directly:
select isbn('978-0-393-04002-9');
select isbn13('0901690546');
select issn('1436-4522');
--Casting types:
-- note that you can't cast from ean13 to other type, thus the following
-- will NOT work: select upc(ean13('0220356483481'));
select ean13(upc('220356483481'));
--Create a table with a single column to hold ISBN numbers:
create table test ( id isbn );
insert into test values('9780393040029');
--Automatically calculating check digits (observe the '?'):
insert into test values('220500896?');
insert into test values('978055215372?');
select issn('3251231?');
select ismn('979047213542?');
--Using the weak mode:
select isn_weak(true);
insert into test values('978-0-11-000533-4');
insert into test values('9780141219307');
insert into test values('2-205-00876-X');
select isn_weak(false);
select id from test where not is_valid(id);
update test set id=make_valid(id) where id = '2-205-00876-X!';
select * from test;
select isbn13(id) from test;
Contact
-------------------------------------------------
Please suggestions or bug reports to kronuz at users.sourceforge.net
Last reviewed on August 23, 2006 by Kronuz.

28
contrib/isn/UPC.h Normal file
View File

@ -0,0 +1,28 @@
/*
* ISSN.h
* PostgreSQL type definitions for ISNs (ISBN, ISMN, ISSN, EAN13, UPC)
*
* No information available for UPC prefixes
*
*
* IDENTIFICATION
* $PostgreSQL: pgsql/contrib/isn/UPC.h,v 1.1 2006/09/09 04:07:52 tgl Exp $
*
*/
/* where the digit set begins, and how many of them are in the table */
const unsigned UPC_index[10][2] = {
{0, 0},
{0, 0},
{0, 0},
{0, 0},
{0, 0},
{0, 0},
{0, 0},
{0, 0},
{0, 0},
{0, 0},
};
const char *UPC_range[][2] = {
{NULL, NULL}
};

910
contrib/isn/isn.c Normal file
View File

@ -0,0 +1,910 @@
/*-------------------------------------------------------------------------
*
* isn.c
* PostgreSQL type definitions for ISNs (ISBN, ISMN, ISSN, EAN13, UPC)
*
* Copyright (c) 2004-2006, Germán Méndez Bravo (Kronuz)
* Portions Copyright (c) 1996-2004, PostgreSQL Global Development Group
*
* IDENTIFICATION
* $PostgreSQL: pgsql/contrib/isn/isn.c,v 1.1 2006/09/09 04:07:52 tgl Exp $
*
*-------------------------------------------------------------------------
*/
#include "postgres.h"
#include "fmgr.h"
#include "utils/builtins.h"
PG_MODULE_MAGIC;
#include "isn.h"
#include "EAN13.h"
#include "ISBN.h"
#include "ISMN.h"
#include "ISSN.h"
#include "UPC.h"
#define MAXEAN13LEN 18
enum isn_type { INVALID, ANY, EAN13, ISBN, ISMN, ISSN, UPC };
static const char *isn_names[] = { "ISN", "ISN", "EAN13", "ISBN", "ISMN", "ISSN", "UPC" };
static bool g_weak = false;
static bool g_initialized = false;
/* Macros for converting TEXT to and from c-string */
#define GET_TEXT(cstrp) DatumGetTextP(DirectFunctionCall1(textin, CStringGetDatum(cstrp)))
#define GET_STR(textp) DatumGetCString(DirectFunctionCall1(textout, PointerGetDatum(textp)))
/***********************************************************************
**
** Routines for ISNs.
**
** Note:
** In this code, a normalized string is one that is known to be a valid
** ISN number containing only digits and hyphens and with enough space
** to hold the full 13 digits plus the maximum of four hyphens.
***********************************************************************/
/*----------------------------------------------------------
* Debugging routines.
*---------------------------------------------------------*/
/*
* Check if the table and its index is correct (just for debugging)
*/
#ifdef ISN_DEBUG
static
bool check_table(const char *(*TABLE)[2], const unsigned TABLE_index[10][2])
{
const char *aux1, *aux2;
int a, b, x=0, y=-1, i=0, j, cnt=0, init=0;
if(TABLE == NULL || TABLE_index == NULL) return true;
while(TABLE[i][0] && TABLE[i][1]) {
aux1 = TABLE[i][0];
aux2 = TABLE[i][1];
/* must always start with a digit: */
if(!isdigit(*aux1) || !isdigit(*aux2)) goto invalidtable;
a = *aux1 - '0';
b = *aux2 - '0';
/* must always have the same format and length: */
while(*aux1 && *aux2) {
if(!(isdigit(*aux1) && isdigit(*aux2)) && (*aux1!=*aux2 || *aux1 != '-'))
goto invalidtable;
aux1++;
aux2++;
}
if(*aux1!=*aux2) goto invalidtable;
/* found a new range */
if(a>y) {
/* check current range in the index: */
for(j=x;j<=y;j++) {
if(TABLE_index[j][0] != init) goto invalidindex;
if(TABLE_index[j][1] != i-init) goto invalidindex;
}
init = i;
x = a;
}
/* Always get the new limit */
y = b;
if(y<x) goto invalidtable;
i++;
}
return true;
invalidtable:
elog(DEBUG1, "invalid table near {\"%s\", \"%s\"} (pos: %d)",
TABLE[i][0], TABLE[i][1], i);
return false;
invalidindex:
elog(DEBUG1, "index %d is invalid", j);
return false;
}
#endif /* ISN_DEBUG */
/*----------------------------------------------------------
* Formatting and conversion routines.
*---------------------------------------------------------*/
static
unsigned dehyphenate(char *bufO, char *bufI)
{
unsigned ret = 0;
while(*bufI) {
if(isdigit(*bufI)) {
*bufO++ = *bufI;
ret++;
}
bufI++;
}
*bufO = '\0';
return ret;
}
/*
* hyphenate --- Try to hyphenate, in-place, the string starting at bufI
* into bufO using the given hyphenation range TABLE.
* Assumes the input string to be used is of only digits.
*
* Returns the number of characters acctually hyphenated.
*/
static
unsigned hyphenate(char *bufO, char *bufI, const char *(*TABLE)[2], const unsigned TABLE_index[10][2])
{
unsigned ret = 0;
const char *ean_aux1, *ean_aux2, *ean_p;
char *firstdig, *aux1, *aux2;
unsigned search, upper, lower, step;
bool ean_in1, ean_in2;
/* just compress the string if no further hyphenation is required */
if(TABLE == NULL || TABLE_index == NULL) {
while(*bufI) {
*bufO++ = *bufI++;
ret++;
}
*bufO = '\0';
return (ret+1);
}
/* add remaining hyphenations */
search = *bufI - '0';
upper = lower = TABLE_index[search][0];
upper += TABLE_index[search][1];
lower--;
step = (upper - lower) / 2;
if(step == 0) return 0;
search = lower + step;
firstdig = bufI;
ean_in1 = ean_in2 = false;
ean_aux1 = TABLE[search][0];
ean_aux2 = TABLE[search][1];
do {
if((ean_in1 || *firstdig>=*ean_aux1) && (ean_in2 || *firstdig<=*ean_aux2)) {
if(*firstdig > *ean_aux1) ean_in1 = true;
if(*firstdig < *ean_aux2) ean_in2 = true;
if(ean_in1 && ean_in2) break;
firstdig++, ean_aux1++, ean_aux2++;
if(!(*ean_aux1 && *ean_aux2 && *firstdig)) break;
if(!isdigit(*ean_aux1)) ean_aux1++, ean_aux2++;
} else {
/* check in what direction we should go and move the pointer accordingly */
if(*firstdig < *ean_aux1 && !ean_in1) upper = search;
else lower = search;
step = (upper - lower) / 2;
search = lower + step;
/* Initialize stuff again: */
firstdig = bufI;
ean_in1 = ean_in2 = false;
ean_aux1 = TABLE[search][0];
ean_aux2 = TABLE[search][1];
}
} while(step);
if(step) {
aux1 = bufO;
aux2 = bufI;
ean_p = TABLE[search][0];
while(*ean_p && *aux2) {
if(*ean_p++!='-') *aux1++ = *aux2++;
else *aux1++ = '-';
ret++;
}
*aux1++='-';
*aux1 = *aux2; /* add a lookahead char */
return (ret+1);
}
return ret;
}
/*
* weight_checkdig -- Receives a buffer with a normalized ISN string number,
* and the length to weight.
*
* Returns the weight of the number (the check digit value, 0-10)
*/
static
unsigned weight_checkdig(char *isn, unsigned size)
{
unsigned weight = 0;
while(*isn && size>1) {
if(isdigit(*isn)) {
weight += size-- * (*isn - '0');
}
isn++;
}
weight = weight % 11;
if(weight != 0) weight = 11 - weight;
return weight;
}
/*
* checkdig --- Receives a buffer with a normalized ISN string number,
* and the length to check.
*
* Returns the check digit value (0-9)
*/
static
unsigned checkdig(char *num, unsigned size)
{
unsigned check=0, check3=0;
unsigned pos = 0;
if(*num == 'M') { /* ISMN start with 'M' */
check3 = 3;
pos = 1;
}
while(*num && size>1) {
if(isdigit(*num)) {
if(pos++%2) check3 += *num - '0';
else check += *num - '0';
size--;
}
num++;
}
check = (check + 3*check3) % 10;
if(check != 0) check = 10 - check;
return check;
}
/*
* ean2isn --- Convert in-place a normalized EAN13 string to the corresponding
* ISN string number. Assumes the input string is normalized.
*/
static inline
void ean2ISBN(char *isn)
{
char *aux;
unsigned check;
/* the number should come in this format: 978-0-000-00000-0 */
/* Strip the first part and calculate the new check digit */
hyphenate(isn, isn+4, NULL, NULL);
check = weight_checkdig(isn, 10);
aux = strchr(isn, '\0');
while(!isdigit(*--aux));
if(check == 10) *aux = 'X';
else *aux = check + '0';
}
static inline
void ean2ISMN(char *isn)
{
/* the number should come in this format: 979-0-000-00000-0 */
/* Just strip the first part and change the first digit ('0') to 'M' */
hyphenate(isn, isn+4, NULL, NULL);
isn[0] = 'M';
}
static inline
void ean2ISSN(char *isn)
{
unsigned check;
/* the number should come in this format: 977-0000-000-00-0 */
/* Strip the first part, crop, and calculate the new check digit */
hyphenate(isn, isn+4, NULL, NULL);
check = weight_checkdig(isn, 8);
if(check == 10) isn[8] = 'X';
else isn[8] = check + '0';
isn[9] = '\0';
}
static inline
void ean2UPC(char *isn)
{
/* the number should come in this format: 000-000000000-0 */
/* Strip the first part, crop, and dehyphenate */
dehyphenate(isn, isn+1);
isn[12] = '\0';
}
/*
* ean2* --- Converts a string of digits into an ean13 number.
* Assumes the input string is a string with only digits
* on it, and that it's within the range of ean13.
*
* Returns the ean13 value of the string.
*/
static
ean13 str2ean(const char *num)
{
ean13 ean = 0; /* current ean */
while(*num) {
ean = 10 * ean + ((*num++) - '0');
}
return (ean<<1); /* also give room to a flag */
}
/*
* ean2string --- Try to convert an ean13 number to an hyphenated string.
* Assumes there's enough space in result to hold
* the string (maximum MAXEAN13LEN+1 bytes)
* This doesn't verify for a valid check digit.
*
* If shortType is true, the returned string is in the old ISN short format.
* If errorOK is false, ereport a useful error message if the string is bad.
* If errorOK is true, just return "false" for bad input.
*/
static
bool ean2string(ean13 ean, bool errorOK, char *result, bool shortType)
{
const char *(*TABLE)[2];
const unsigned (*TABLE_index)[2];
enum isn_type type = INVALID;
char *firstdig, *aux;
unsigned digval;
unsigned search;
char valid = '\0'; /* was the number initially written with a valid check digit? */
TABLE_index = ISBN_index;
if((ean & 1)!=0) valid = '!';
ean >>= 1;
/* verify it's in the EAN13 range */
if(ean > UINT64CONST(9999999999999))
goto eantoobig;
/* convert the number */
search = 0;
firstdig = aux = result + MAXEAN13LEN;
*aux = '\0'; /* terminate string; aux points to last digit */
*--aux = valid; /* append '!' for numbers with invalid but corrected check digit */
do {
digval = (unsigned)(ean % 10); /* get the decimal value */
ean /= 10; /* get next digit */
*--aux = (char)(digval + '0'); /* convert to ascii and store */
if(++search == 1) *--aux = '-'; /* the check digit is always there */
} while(ean);
while(search++<13) *--aux = '0'; /* fill the remaining EAN13 with '0' */
/* The string should be in this form: ???DDDDDDDDDDDD-D" */
search = hyphenate(result, result+3, EAN13_range, EAN13_index);
/* verify it's a logically valid EAN13 */
if(search == 0) {
search = hyphenate(result, result+3, NULL, NULL);
goto okay;
}
/* find out what type of hyphenation is needed: */
if(!strncmp("978-", result, search)) { /* ISBN */
/* The string should be in this form: 978-??000000000-0" */
type = ISBN;
TABLE = ISBN_range;
TABLE_index = ISBN_index;
} else if(!strncmp("977-", result, search)) { /* ISSN */
/* The string should be in this form: 977-??000000000-0" */
type = ISSN;
TABLE = ISSN_range;
TABLE_index = ISSN_index;
} else if(!strncmp("979-0", result, search+1)) { /* ISMN */
/* The string should be in this form: 979-0?000000000-0" */
type = ISMN;
TABLE = ISMN_range;
TABLE_index = ISMN_index;
} else if(*result == '0') { /* UPC */
/* The string should be in this form: 000-00000000000-0" */
type = UPC;
TABLE = UPC_range;
TABLE_index = UPC_index;
} else {
type = EAN13;
TABLE = NULL;
TABLE_index = NULL;
}
/* verify it's a logically valid EAN13/ISN */
digval = search;
search = hyphenate(result+digval, result+digval+2, TABLE, TABLE_index);
/* verify it's a valid EAN13 */
if(search == 0) {
search = hyphenate(result+digval, result+digval+2, NULL, NULL);
goto okay;
}
okay:
/* convert to the old short type: */
if(shortType)
switch(type) {
case ISBN:
ean2ISBN(result);
break;
case ISMN:
ean2ISMN(result);
break;
case ISSN:
ean2ISSN(result);
break;
case UPC:
ean2UPC(result);
break;
default:
break;
}
return true;
eantoobig:
if(!errorOK)
{
char eanbuf[64];
/*
* Format the number separately to keep the machine-dependent
* format code out of the translatable message text
*/
snprintf(eanbuf, sizeof(eanbuf), EAN13_FORMAT, ean);
ereport(ERROR,
(errcode(ERRCODE_NUMERIC_VALUE_OUT_OF_RANGE),
errmsg("value \"%s\" is out of range for ISN type",
eanbuf)));
}
return false;
}
/*
* string2ean --- try to parse a string into an ean13.
*
* If errorOK is false, ereport a useful error message if the string is bad.
* If errorOK is true, just return "false" for bad input.
*
* if the input string ends with '!' it will always be treated as invalid
* (even if the check digit is valid)
*/
static
bool string2ean(const char *str, bool errorOK, ean13 *result,
enum isn_type accept)
{
bool digit, last;
char buf[17] = " ";
char *aux1 = buf + 3; /* leave space for the first part, in case it's needed */
const char *aux2 = str;
enum isn_type type = INVALID;
unsigned check = 0, rcheck = (unsigned)-1;
unsigned length = 0;
bool magic = false, valid = true;
/* recognize and validate the number: */
while(*aux2 && length <= 13) {
last = (*(aux2+1) == '!' || *(aux2+1) == '\0'); /* is the last character */
digit = isdigit(*aux2); /* is current character a digit? */
if(*aux2=='?' && last) /* automagically calculate check digit if it's '?' */
magic = digit = true;
if(length == 0 && (*aux2=='M' || *aux2=='m')) {
/* only ISMN can be here */
if(type != INVALID) goto eaninvalid;
type = ISMN;
*aux1++ = 'M';
length++;
} else if(length == 7 && (digit || *aux2=='X' || *aux2=='x') && last) {
/* only ISSN can be here */
if(type != INVALID) goto eaninvalid;
type = ISSN;
*aux1++ = toupper(*aux2);
length++;
} else if(length == 9 && (digit || *aux2=='X' || *aux2=='x') && last) {
/* only ISBN and ISMN can be here */
if(type != INVALID && type != ISMN) goto eaninvalid;
if(type == INVALID) type = ISBN; /* ISMN must start with 'M' */
*aux1++ = toupper(*aux2);
length++;
} else if(length == 11 && digit && last) {
/* only UPC can be here */
if(type != INVALID) goto eaninvalid;
type = UPC;
*aux1++ = *aux2;
length++;
} else if(*aux2 == '-' || *aux2 == ' ') {
/* skip, we could validate but I think it's worthless */
} else if(*aux2 == '!' && *(aux2+1) == '\0') {
/* the invalid check digit sufix was found, set it */
if(!magic) valid = false;
magic = true;
} else if(!digit) {
goto eaninvalid;
} else {
*aux1++ = *aux2;
if(++length > 13) goto eantoobig;
}
aux2++;
}
*aux1 = '\0'; /* terminate the string */
/* find the current check digit value */
if(length == 13) {
/* only EAN13 can be here */
if(type != INVALID) goto eaninvalid;
type = EAN13;
check = buf[15]-'0';
} else if(length == 12) {
/* only UPC can be here */
if(type != UPC) goto eaninvalid;
check = buf[14]-'0';
} else if(length == 10) {
if(type != ISBN && type != ISMN) goto eaninvalid;
if(buf[12] == 'X') check = 10;
else check = buf[12]-'0';
} else if(length == 8) {
if(type != INVALID && type != ISSN) goto eaninvalid;
type = ISSN;
if(buf[10] == 'X') check = 10;
else check = buf[10]-'0';
} else goto eaninvalid;
if(type == INVALID) goto eaninvalid;
/* obtain the real check digit value, validate, and convert to ean13: */
if(accept == EAN13 && type != accept) goto eanwrongtype;
if(accept != ANY && type != EAN13 && type != accept) goto eanwrongtype;
switch(type) {
case EAN13:
valid = (valid && ((rcheck=checkdig(buf+3, 13)) == check || magic));
/* now get the subtype of EAN13: */
if(buf[3] == '0') type = UPC;
else if(!strncmp("977", buf+3, 3)) type = ISSN;
else if(!strncmp("978", buf+3, 3)) type = ISBN;
else if(!strncmp("9790", buf+3, 4)) type = ISMN;
else if(!strncmp("979", buf+3, 3)) type = ISBN;
if(accept != EAN13 && accept != ANY && type != accept) goto eanwrongtype;
break;
case ISMN:
strncpy(buf, "9790", 4); /* this isn't for sure yet, for now ISMN it's only 9790 */
valid = (valid && ((rcheck=checkdig(buf+3, 10)) == check || magic));
break;
case ISBN:
strncpy(buf, "978", 3);
valid = (valid && ((rcheck=weight_checkdig(buf+3, 10)) == check || magic));
break;
case ISSN:
strncpy(buf+10, "00", 2); /* append 00 as the normal issue publication code */
strncpy(buf, "977", 3);
valid = (valid && ((rcheck=weight_checkdig(buf+3, 8)) == check || magic));
break;
case UPC:
buf[2] = '0';
valid = (valid && ((rcheck=checkdig(buf+2, 13)) == check || magic));
default:
break;
}
if(!valid && !magic) goto eanbadcheck;
for(aux1 = buf; *aux1 && *aux1 <= ' '; aux1++);
aux1[12] = checkdig(aux1, 13) + '0';
aux1[13] = '\0';
*result = str2ean(aux1);
*result |= valid?0:1;
return true;
eanbadcheck:
if(!g_weak) {
if(!errorOK) {
if(rcheck == (unsigned)-1) {
ereport(ERROR,
(errcode(ERRCODE_INVALID_TEXT_REPRESENTATION),
errmsg("invalid %s number: \"%s\"",
isn_names[accept], str)));
} else {
ereport(ERROR,
(errcode(ERRCODE_INVALID_TEXT_REPRESENTATION),
errmsg("invalid check digit for %s number: \"%s\", should be %c",
isn_names[accept], str, (rcheck==10)?('X'):(rcheck+'0'))));
}
}
return false;
}
if(accept != EAN13 && accept != ANY && type != accept) goto eanwrongtype;
/* fix the check digit: */
for(aux1 = buf; *aux1 && *aux1 <= ' '; aux1++);
aux1[12] = checkdig(aux1, 13) + '0';
aux1[13] = '\0';
*result = str2ean(aux1);
/* set the "invalid-check-digit-on-input" flag */
*result |= 1;
/* just warn about the error when there was a real check digit error: */
if(check != rcheck) {
if(rcheck == (unsigned)-1) {
elog(WARNING, "invalid %s number: \"%s\"",
isn_names[accept], str);
} else {
elog(WARNING, "invalid check digit for %s number: \"%s\", should be %c",
isn_names[accept], str, (rcheck==10)?('X'):(rcheck+'0'));
}
}
return true;
eaninvalid:
if(!errorOK)
ereport(ERROR,
(errcode(ERRCODE_INVALID_TEXT_REPRESENTATION),
errmsg("invalid input syntax for %s number: \"%s\"",
isn_names[accept], str)));
return false;
eanwrongtype:
if(!errorOK)
ereport(ERROR,
(errcode(ERRCODE_INVALID_TEXT_REPRESENTATION),
errmsg("invalid %s type for number: \"%s\"",
isn_names[accept], str)));
return false;
eantoobig:
if(!errorOK)
ereport(ERROR,
(errcode(ERRCODE_NUMERIC_VALUE_OUT_OF_RANGE),
errmsg("value \"%s\" is out of range for %s type",
str, isn_names[accept])));
return false;
}
/*----------------------------------------------------------
* Exported routines.
*---------------------------------------------------------*/
void initialize(void)
{
#ifdef ISN_DEBUG
if(!check_table(EAN13, EAN13_index))
elog(LOG, "EAN13 failed check");
if(!check_table(ISBN, ISBN_index))
elog(LOG, "ISBN failed check");
if(!check_table(ISMN, ISMN_index))
elog(LOG, "ISMN failed check");
if(!check_table(ISSN, ISSN_index))
elog(LOG, "ISSN failed check");
if(!check_table(UPC, UPC_index))
elog(LOG, "UPC failed check");
#endif
g_initialized = true;
}
/* isn_out
*/
PG_FUNCTION_INFO_V1(isn_out);
Datum
isn_out(PG_FUNCTION_ARGS)
{
ean13 val = PG_GETARG_EAN13(0);
char *result;
char buf[MAXEAN13LEN + 1];
(void) ean2string(val, false, buf, true);
result = pstrdup(buf);
PG_RETURN_CSTRING(result);
}
/* ean13_out
*/
PG_FUNCTION_INFO_V1(ean13_out);
Datum
ean13_out(PG_FUNCTION_ARGS)
{
ean13 val = PG_GETARG_EAN13(0);
char *result;
char buf[MAXEAN13LEN + 1];
(void) ean2string(val, false, buf, false);
result = pstrdup(buf);
PG_RETURN_CSTRING(result);
}
/* ean13_in
*/
PG_FUNCTION_INFO_V1(ean13_in);
Datum
ean13_in(PG_FUNCTION_ARGS)
{
const char *str = PG_GETARG_CSTRING(0);
ean13 result;
(void) string2ean(str, false, &result, EAN13);
PG_RETURN_EAN13(result);
}
/* isbn_in
*/
PG_FUNCTION_INFO_V1(isbn_in);
Datum
isbn_in(PG_FUNCTION_ARGS)
{
const char *str = PG_GETARG_CSTRING(0);
ean13 result;
(void) string2ean(str, false, &result, ISBN);
PG_RETURN_EAN13(result);
}
/* ismn_in
*/
PG_FUNCTION_INFO_V1(ismn_in);
Datum
ismn_in(PG_FUNCTION_ARGS)
{
const char *str = PG_GETARG_CSTRING(0);
ean13 result;
(void) string2ean(str, false, &result, ISMN);
PG_RETURN_EAN13(result);
}
/* issn_in
*/
PG_FUNCTION_INFO_V1(issn_in);
Datum
issn_in(PG_FUNCTION_ARGS)
{
const char *str = PG_GETARG_CSTRING(0);
ean13 result;
(void) string2ean(str, false, &result, ISSN);
PG_RETURN_EAN13(result);
}
/* upc_in
*/
PG_FUNCTION_INFO_V1(upc_in);
Datum
upc_in(PG_FUNCTION_ARGS)
{
const char *str = PG_GETARG_CSTRING(0);
ean13 result;
(void) string2ean(str, false, &result, UPC);
PG_RETURN_EAN13(result);
}
/* casting functions
*/
PG_FUNCTION_INFO_V1(ean13_cast_to_text);
Datum
ean13_cast_to_text(PG_FUNCTION_ARGS)
{
ean13 val = PG_GETARG_EAN13(0);
char buf[MAXEAN13LEN + 1];
(void) ean2string(val, false, buf, false);
PG_RETURN_TEXT_P(GET_TEXT(buf));
}
PG_FUNCTION_INFO_V1(isn_cast_to_text);
Datum
isn_cast_to_text(PG_FUNCTION_ARGS)
{
ean13 val = PG_GETARG_EAN13(0);
char buf[MAXEAN13LEN + 1];
(void) ean2string(val, false, buf, true);
PG_RETURN_TEXT_P(GET_TEXT(buf));
}
PG_FUNCTION_INFO_V1(ean13_cast_from_text);
Datum
ean13_cast_from_text(PG_FUNCTION_ARGS)
{
const char *str = GET_STR(PG_GETARG_TEXT_P(0));
ean13 result;
(void) string2ean(str, false, &result, EAN13);
PG_RETURN_EAN13(result);
}
PG_FUNCTION_INFO_V1(isbn_cast_from_text);
Datum
isbn_cast_from_text(PG_FUNCTION_ARGS)
{
const char *str = GET_STR(PG_GETARG_TEXT_P(0));
ean13 result;
(void) string2ean(str, false, &result, ISBN);
PG_RETURN_EAN13(result);
}
PG_FUNCTION_INFO_V1(ismn_cast_from_text);
Datum
ismn_cast_from_text(PG_FUNCTION_ARGS)
{
const char *str = GET_STR(PG_GETARG_TEXT_P(0));
ean13 result;
(void) string2ean(str, false, &result, ISMN);
PG_RETURN_EAN13(result);
}
PG_FUNCTION_INFO_V1(issn_cast_from_text);
Datum
issn_cast_from_text(PG_FUNCTION_ARGS)
{
const char *str = GET_STR(PG_GETARG_TEXT_P(0));
ean13 result;
(void) string2ean(str, false, &result, ISSN);
PG_RETURN_EAN13(result);
}
PG_FUNCTION_INFO_V1(upc_cast_from_text);
Datum
upc_cast_from_text(PG_FUNCTION_ARGS)
{
const char *str = GET_STR(PG_GETARG_TEXT_P(0));
ean13 result;
(void) string2ean(str, false, &result, UPC);
PG_RETURN_EAN13(result);
}
/* is_valid - returns false if the "invalid-check-digit-on-input" is set
*/
PG_FUNCTION_INFO_V1(is_valid);
Datum
is_valid(PG_FUNCTION_ARGS)
{
ean13 val = PG_GETARG_EAN13(0);
PG_RETURN_BOOL((val & 1) == 0);
}
/* make_valid - unsets the "invalid-check-digit-on-input" flag
*/
PG_FUNCTION_INFO_V1(make_valid);
Datum
make_valid(PG_FUNCTION_ARGS)
{
ean13 val = PG_GETARG_EAN13(0);
val &= ~((ean13) 1);
PG_RETURN_EAN13(val);
}
#ifdef ISN_WEAK_MODE
/* this function temporarily sets weak input flag
* (to lose the strictness of check digit acceptance)
* It's a helper function, not intended to be used!!
*/
PG_FUNCTION_INFO_V1(accept_weak_input);
Datum
accept_weak_input(PG_FUNCTION_ARGS)
{
g_weak = PG_GETARG_BOOL(0);
PG_RETURN_BOOL(g_weak);
}
#else
PG_FUNCTION_INFO_V1(accept_weak_input);
Datum
accept_weak_input(PG_FUNCTION_ARGS)
{
/* function has no effect */
PG_RETURN_BOOL(false);
}
#endif /* ISN_WEAK_MODE */
PG_FUNCTION_INFO_V1(weak_input_status);
Datum
weak_input_status(PG_FUNCTION_ARGS)
{
PG_RETURN_BOOL(g_weak);
}

57
contrib/isn/isn.h Normal file
View File

@ -0,0 +1,57 @@
/*-------------------------------------------------------------------------
*
* isn.h
* PostgreSQL type definitions for ISNs (ISBN, ISMN, ISSN, EAN13, UPC)
*
* Copyright (c) 2004-2006, Germán Méndez Bravo (Kronuz)
* Portions Copyright (c) 1996-2004, PostgreSQL Global Development Group
*
* IDENTIFICATION
* $PostgreSQL: pgsql/contrib/isn/isn.h,v 1.1 2006/09/09 04:07:52 tgl Exp $
*
*-------------------------------------------------------------------------
*/
#ifndef ISN_H
#define ISN_H
#include "fmgr.h"
#undef ISN_DEBUG
#define ISN_WEAK_MODE
/*
* uint64 is the internal storage format for ISNs.
*/
typedef uint64 ean13;
#define EAN13_FORMAT UINT64_FORMAT
#define PG_GETARG_EAN13(n) PG_GETARG_INT64(n)
#define PG_RETURN_EAN13(x) PG_RETURN_INT64(x)
extern Datum isn_out(PG_FUNCTION_ARGS);
extern Datum ean13_out(PG_FUNCTION_ARGS);
extern Datum ean13_in(PG_FUNCTION_ARGS);
extern Datum isbn_in(PG_FUNCTION_ARGS);
extern Datum ismn_in(PG_FUNCTION_ARGS);
extern Datum issn_in(PG_FUNCTION_ARGS);
extern Datum upc_in(PG_FUNCTION_ARGS);
extern Datum ean13_cast_to_text(PG_FUNCTION_ARGS);
extern Datum isn_cast_to_text(PG_FUNCTION_ARGS);
extern Datum ean13_cast_from_text(PG_FUNCTION_ARGS);
extern Datum isbn_cast_from_text(PG_FUNCTION_ARGS);
extern Datum ismn_cast_from_text(PG_FUNCTION_ARGS);
extern Datum issn_cast_from_text(PG_FUNCTION_ARGS);
extern Datum upc_cast_from_text(PG_FUNCTION_ARGS);
extern Datum is_valid(PG_FUNCTION_ARGS);
extern Datum make_valid(PG_FUNCTION_ARGS);
extern Datum accept_weak_input(PG_FUNCTION_ARGS);
extern Datum weak_input_status(PG_FUNCTION_ARGS);
extern void initialize(void);
#endif /* ISN_H */

2350
contrib/isn/isn.sql.in Normal file

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,18 @@
--
-- Drop the actual types (in cascade):
--
---------------------------------------------------
SET search_path = public;
DROP TYPE ean13 CASCADE;
DROP TYPE isbn13 CASCADE;
DROP TYPE ismn13 CASCADE;
DROP TYPE issn13 CASCADE;
DROP TYPE isbn CASCADE;
DROP TYPE ismn CASCADE;
DROP TYPE issn CASCADE;
DROP TYPE upc CASCADE;
DROP FUNCTION isn_weak();
DROP FUNCTION isn_weak(boolean);